diff --git a/src/circom_adapter.cpp b/src/circom_adapter.cpp index c2a590d..0560753 100644 --- a/src/circom_adapter.cpp +++ b/src/circom_adapter.cpp @@ -4,6 +4,17 @@ #include #include #include +#include + +Circom_Circuit* getCachedCircuit(const ConstBytes& circuit_bytes) { + // The circuit is immutable, compiled-in data and has no destructor that + // frees its internal buffers, so load it once and keep it for the process + // lifetime instead of allocating (and leaking) it on every call. + static Circom_Circuit* cached = nullptr; + static std::once_flag once; + std::call_once(once, [&]() { cached = loadCircuit(circuit_bytes); }); + return cached; +} Circom_Circuit* loadCircuit(const ConstBytes& circuit_bytes) { Circom_Circuit* circuit = new Circom_Circuit; diff --git a/src/circom_adapter.hpp b/src/circom_adapter.hpp index e90e0c6..bd4798f 100644 --- a/src/circom_adapter.hpp +++ b/src/circom_adapter.hpp @@ -7,6 +7,16 @@ // Return value Circom_Circuit* loadCircuit(const ConstBytes& circuit); + +// Returns a process-wide, lazily-loaded circuit for this library. +// +// `loadCircuit` allocates the circuit's internal buffers (input hash map, +// witness->signal list, constants, IO field defs) but `Circom_Circuit` has no +// destructor, so `delete circuit` leaks all of them. The circuit is immutable +// data derived from the compiled-in `.dat`, so we load it exactly once and +// reuse it for every (including concurrent) witness-generation call. +Circom_Circuit* getCachedCircuit(const ConstBytes& circuit); + void loadJson(Circom_CalcWit *ctx, const char* inputs_json); void writeBinWitness(Circom_CalcWit *ctx, Bytes* output_witness); diff --git a/src/poc/ffi.cpp b/src/poc/ffi.cpp index d5a25d4..5ec2244 100644 --- a/src/poc/ffi.cpp +++ b/src/poc/ffi.cpp @@ -89,26 +89,23 @@ static Status validate_witness_arguments(const WitnessInput* input, const Bytes* static Status generate_witness_impl(const WitnessInput* input, Bytes* output) { const ConstBytes& circuit_bytes = input->dat; - Circom_Circuit* circuit = loadCircuit(circuit_bytes); + Circom_Circuit* circuit = getCachedCircuit(circuit_bytes); Circom_CalcWit* ctx = new Circom_CalcWit(circuit); try { loadJson(ctx, input->inputs_json); } catch (...) { delete ctx; - delete circuit; throw; } if (ctx->getRemaingInputsToBeSet()!=0) { const std::string message = "Not all inputs have been set. Only " + std::to_string(get_main_input_signal_no()-ctx->getRemaingInputsToBeSet()) + " out of " + std::to_string(get_main_input_signal_no()) + "."; delete ctx; - delete circuit; return status_new(StatusCode_InvalidInput, message.c_str()); } writeBinWitness(ctx, output); delete ctx; - delete circuit; return status_ok(); } diff --git a/src/pol/ffi.cpp b/src/pol/ffi.cpp index 4a9cac2..176f3f9 100644 --- a/src/pol/ffi.cpp +++ b/src/pol/ffi.cpp @@ -89,26 +89,23 @@ static Status validate_witness_arguments(const WitnessInput* input, const Bytes* static Status generate_witness_impl(const WitnessInput* input, Bytes* output) { const ConstBytes& circuit_bytes = input->dat; - Circom_Circuit* circuit = loadCircuit(circuit_bytes); + Circom_Circuit* circuit = getCachedCircuit(circuit_bytes); Circom_CalcWit* ctx = new Circom_CalcWit(circuit); try { loadJson(ctx, input->inputs_json); } catch (...) { delete ctx; - delete circuit; throw; } if (ctx->getRemaingInputsToBeSet()!=0) { const std::string message = "Not all inputs have been set. Only " + std::to_string(get_main_input_signal_no()-ctx->getRemaingInputsToBeSet()) + " out of " + std::to_string(get_main_input_signal_no()) + "."; delete ctx; - delete circuit; return status_new(StatusCode_InvalidInput, message.c_str()); } writeBinWitness(ctx, output); delete ctx; - delete circuit; return status_ok(); } diff --git a/src/poq/ffi.cpp b/src/poq/ffi.cpp index 7715c89..44c69ab 100644 --- a/src/poq/ffi.cpp +++ b/src/poq/ffi.cpp @@ -89,26 +89,23 @@ static Status validate_witness_arguments(const WitnessInput* input, const Bytes* static Status generate_witness_impl(const WitnessInput* input, Bytes* output) { const ConstBytes& circuit_bytes = input->dat; - Circom_Circuit* circuit = loadCircuit(circuit_bytes); + Circom_Circuit* circuit = getCachedCircuit(circuit_bytes); Circom_CalcWit* ctx = new Circom_CalcWit(circuit); try { loadJson(ctx, input->inputs_json); } catch (...) { delete ctx; - delete circuit; throw; } if (ctx->getRemaingInputsToBeSet()!=0) { const std::string message = "Not all inputs have been set. Only " + std::to_string(get_main_input_signal_no()-ctx->getRemaingInputsToBeSet()) + " out of " + std::to_string(get_main_input_signal_no()) + "."; delete ctx; - delete circuit; return status_new(StatusCode_InvalidInput, message.c_str()); } writeBinWitness(ctx, output); delete ctx; - delete circuit; return status_ok(); } diff --git a/src/signature/ffi.cpp b/src/signature/ffi.cpp index 94a7990..43a792b 100644 --- a/src/signature/ffi.cpp +++ b/src/signature/ffi.cpp @@ -89,26 +89,23 @@ static Status validate_witness_arguments(const WitnessInput* input, const Bytes* static Status generate_witness_impl(const WitnessInput* input, Bytes* output) { const ConstBytes& circuit_bytes = input->dat; - Circom_Circuit* circuit = loadCircuit(circuit_bytes); + Circom_Circuit* circuit = getCachedCircuit(circuit_bytes); Circom_CalcWit* ctx = new Circom_CalcWit(circuit); try { loadJson(ctx, input->inputs_json); } catch (...) { delete ctx; - delete circuit; throw; } if (ctx->getRemaingInputsToBeSet()!=0) { const std::string message = "Not all inputs have been set. Only " + std::to_string(get_main_input_signal_no()-ctx->getRemaingInputsToBeSet()) + " out of " + std::to_string(get_main_input_signal_no()) + "."; delete ctx; - delete circuit; return status_new(StatusCode_InvalidInput, message.c_str()); } writeBinWitness(ctx, output); delete ctx; - delete circuit; return status_ok(); }