Add other circuits FFIs and smoke tests.

This commit is contained in:
Alejandro Cabeza Romero 2026-04-22 12:41:07 +02:00
parent fdd1305c0c
commit 7a10f70eca
No known key found for this signature in database
GPG Key ID: DA3D14AE478030FD
12 changed files with 727 additions and 3 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ result
*_cpp/
*.r1cs
**/input.json
**/*-input.json

View File

@ -20,7 +20,7 @@ static uint8_t* read_file(const char* path, size_t* out_size) {
int main() {
Status status = poq_generate_witness_from_files(
"poq",
"../input.json",
"../poq-input.json",
"witness.wtns"
);
@ -33,7 +33,7 @@ int main() {
size_t dat_size, json_size;
uint8_t* dat_data = read_file("poq.dat", &dat_size);
uint8_t* json_data = read_file("../input.json", &json_size);
uint8_t* json_data = read_file("../poq-input.json", &json_size);
WitnessInput input = {
{dat_data, dat_size},

View File

@ -30,5 +30,53 @@ test-poq: poq
g++ -std=c++11 -O3 -I blend/poq_cpp blend/poq_cpp/test_ffi.cpp -L blend/poq_cpp -lwitness_poq -lgmp -o blend/poq_cpp/test_ffi
cd blend/poq_cpp && ./test_ffi
# Build the PoL circuit and its C++ witness generator, equivalent to the CI build.
pol:
circom mantle/pol.circom --c --r1cs --no_asm --O2 --output mantle
# circom-generated main() has no return on the success path; patch it before -O3 turns it into an infinite loop
sed -i ':a;N;$!ba;s/\n}\n\n*$/\n return 0;\n}/' mantle/pol_cpp/main.cpp
cp -r {{src}}/pol mantle/pol_cpp/pol
cp {{src}}/circom_adapter.cpp {{src}}/circom_adapter.hpp {{src}}/circom_fwd.hpp {{src}}/types.hpp mantle/pol_cpp/
cp {{ci_makefile}} mantle/pol_cpp/Makefile
cp mantle/test_pol.cpp mantle/pol_cpp/test_pol.cpp
make -C mantle/pol_cpp PROJECT=pol linux-lib
# Run a simple smoke test of the PoL witness generator.
test-pol: pol
g++ -std=c++11 -O3 -I mantle/pol_cpp mantle/pol_cpp/test_pol.cpp -L mantle/pol_cpp -lwitness_pol -lgmp -o mantle/pol_cpp/test_pol
cd mantle/pol_cpp && ./test_pol
# Build the PoC circuit and its C++ witness generator, equivalent to the CI build.
poc:
circom mantle/poc.circom --c --r1cs --no_asm --O2 --output mantle
# circom-generated main() has no return on the success path; patch it before -O3 turns it into an infinite loop
sed -i ':a;N;$!ba;s/\n}\n\n*$/\n return 0;\n}/' mantle/poc_cpp/main.cpp
cp -r {{src}}/poc mantle/poc_cpp/poc
cp {{src}}/circom_adapter.cpp {{src}}/circom_adapter.hpp {{src}}/circom_fwd.hpp {{src}}/types.hpp mantle/poc_cpp/
cp {{ci_makefile}} mantle/poc_cpp/Makefile
cp mantle/test_poc.cpp mantle/poc_cpp/test_poc.cpp
make -C mantle/poc_cpp PROJECT=poc linux-lib
# Run a simple smoke test of the PoC witness generator.
test-poc: poc
g++ -std=c++11 -O3 -I mantle/poc_cpp mantle/poc_cpp/test_poc.cpp -L mantle/poc_cpp -lwitness_poc -lgmp -o mantle/poc_cpp/test_poc
cd mantle/poc_cpp && ./test_poc
# Build the signature circuit and its C++ witness generator, equivalent to the CI build.
signature:
circom mantle/signature.circom --c --r1cs --no_asm --O2 --output mantle
# circom-generated main() has no return on the success path; patch it before -O3 turns it into an infinite loop
sed -i ':a;N;$!ba;s/\n}\n\n*$/\n return 0;\n}/' mantle/signature_cpp/main.cpp
cp -r {{src}}/signature mantle/signature_cpp/signature
cp {{src}}/circom_adapter.cpp {{src}}/circom_adapter.hpp {{src}}/circom_fwd.hpp {{src}}/types.hpp mantle/signature_cpp/
cp {{ci_makefile}} mantle/signature_cpp/Makefile
cp mantle/test_signature.cpp mantle/signature_cpp/test_signature.cpp
make -C mantle/signature_cpp PROJECT=signature linux-lib
# Run a simple smoke test of the signature witness generator.
test-signature: signature
g++ -std=c++11 -O3 -I mantle/signature_cpp mantle/signature_cpp/test_signature.cpp -L mantle/signature_cpp -lwitness_signature -lgmp -o mantle/signature_cpp/test_signature
cd mantle/signature_cpp && ./test_signature
clean:
rm -rf blend/poq_cpp
rm -rf blend/poq_cpp mantle/pol_cpp mantle/poc_cpp mantle/signature_cpp

64
mantle/test_poc.cpp Normal file
View File

@ -0,0 +1,64 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "poc/ffi.hpp"
static uint8_t* read_file(const char* path, size_t* out_size) {
FILE* f = fopen(path, "rb");
if (!f) return nullptr;
fseek(f, 0, SEEK_END);
*out_size = ftell(f);
rewind(f);
uint8_t* buf = (uint8_t*)malloc(*out_size + 1);
fread(buf, 1, *out_size, f);
buf[*out_size] = '\0';
fclose(f);
return buf;
}
int main() {
Status status = poc_generate_witness_from_files(
"poc",
"../poc-input.json",
"witness.wtns"
);
if (status_is_error(status)) {
fprintf(stderr, "Error [%d]: %s\n", status.code, status.message);
return 1;
}
printf("generate_witness_from_files: OK\n");
size_t dat_size, json_size;
uint8_t* dat_data = read_file("poc.dat", &dat_size);
uint8_t* json_data = read_file("../poc-input.json", &json_size);
WitnessInput input = {
{dat_data, dat_size},
(const char*)json_data
};
Bytes output = {nullptr, 0};
status = poc_generate_witness(&input, &output);
free(dat_data);
free(json_data);
if (status_is_error(status)) {
fprintf(stderr, "Error [%d]: %s\n", status.code, status.message);
return 1;
}
size_t wtns_size;
uint8_t* wtns_data = read_file("witness.wtns", &wtns_size);
assert(wtns_data != nullptr);
assert(output.size == wtns_size);
assert(memcmp(output.data, wtns_data, output.size) == 0);
free(wtns_data);
printf("generate_witness: OK (%zu bytes, matches witness.wtns)\n", output.size);
free_bytes(&output);
return 0;
}

64
mantle/test_pol.cpp Normal file
View File

@ -0,0 +1,64 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "pol/ffi.hpp"
static uint8_t* read_file(const char* path, size_t* out_size) {
FILE* f = fopen(path, "rb");
if (!f) return nullptr;
fseek(f, 0, SEEK_END);
*out_size = ftell(f);
rewind(f);
uint8_t* buf = (uint8_t*)malloc(*out_size + 1);
fread(buf, 1, *out_size, f);
buf[*out_size] = '\0';
fclose(f);
return buf;
}
int main() {
Status status = pol_generate_witness_from_files(
"pol",
"../pol-input.json",
"witness.wtns"
);
if (status_is_error(status)) {
fprintf(stderr, "Error [%d]: %s\n", status.code, status.message);
return 1;
}
printf("generate_witness_from_files: OK\n");
size_t dat_size, json_size;
uint8_t* dat_data = read_file("pol.dat", &dat_size);
uint8_t* json_data = read_file("../pol-input.json", &json_size);
WitnessInput input = {
{dat_data, dat_size},
(const char*)json_data
};
Bytes output = {nullptr, 0};
status = pol_generate_witness(&input, &output);
free(dat_data);
free(json_data);
if (status_is_error(status)) {
fprintf(stderr, "Error [%d]: %s\n", status.code, status.message);
return 1;
}
size_t wtns_size;
uint8_t* wtns_data = read_file("witness.wtns", &wtns_size);
assert(wtns_data != nullptr);
assert(output.size == wtns_size);
assert(memcmp(output.data, wtns_data, output.size) == 0);
free(wtns_data);
printf("generate_witness: OK (%zu bytes, matches witness.wtns)\n", output.size);
free_bytes(&output);
return 0;
}

64
mantle/test_signature.cpp Normal file
View File

@ -0,0 +1,64 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "signature/ffi.hpp"
static uint8_t* read_file(const char* path, size_t* out_size) {
FILE* f = fopen(path, "rb");
if (!f) return nullptr;
fseek(f, 0, SEEK_END);
*out_size = ftell(f);
rewind(f);
uint8_t* buf = (uint8_t*)malloc(*out_size + 1);
fread(buf, 1, *out_size, f);
buf[*out_size] = '\0';
fclose(f);
return buf;
}
int main() {
Status status = signature_generate_witness_from_files(
"signature",
"../signature-input.json",
"witness.wtns"
);
if (status_is_error(status)) {
fprintf(stderr, "Error [%d]: %s\n", status.code, status.message);
return 1;
}
printf("generate_witness_from_files: OK\n");
size_t dat_size, json_size;
uint8_t* dat_data = read_file("signature.dat", &dat_size);
uint8_t* json_data = read_file("../signature-input.json", &json_size);
WitnessInput input = {
{dat_data, dat_size},
(const char*)json_data
};
Bytes output = {nullptr, 0};
status = signature_generate_witness(&input, &output);
free(dat_data);
free(json_data);
if (status_is_error(status)) {
fprintf(stderr, "Error [%d]: %s\n", status.code, status.message);
return 1;
}
size_t wtns_size;
uint8_t* wtns_data = read_file("witness.wtns", &wtns_size);
assert(wtns_data != nullptr);
assert(output.size == wtns_size);
assert(memcmp(output.data, wtns_data, output.size) == 0);
free(wtns_data);
printf("generate_witness: OK (%zu bytes, matches witness.wtns)\n", output.size);
free_bytes(&output);
return 0;
}

119
src/poc/ffi.cpp Normal file
View File

@ -0,0 +1,119 @@
#include "poc/ffi.hpp"
#include "circom_fwd.hpp"
#include "circom_adapter.hpp"
#include <string>
#include <algorithm>
#include "../types.hpp"
template<typename T>
static Status exceptions_into_status(T&& func) {
try {
return func();
} catch (const std::bad_alloc&) {
return status_from_code(StatusCode_OutOfMemory);
} catch (const std::exception& e) {
return status_new(StatusCode_DynError, e.what());
} catch (...) {
return status_new(StatusCode_DynError, "An unknown error occurred.");
}
}
static Status validate_generate_witness_from_files_arguments(const char* dat, const char* inputs, const char* output) {
if (dat == nullptr) {
return status_new(StatusCode_InvalidInput, "dat is null.");
}
if (inputs == nullptr) {
return status_new(StatusCode_InvalidInput, "inputs is null.");
}
if (output == nullptr) {
return status_new(StatusCode_InvalidInput, "output is null.");
}
return status_ok();
}
static Status generate_witness_from_files_impl(const char* dat, const char* inputs, const char* output) {
char* argv[] = {
const_cast<char*>(dat),
const_cast<char*>(inputs),
const_cast<char*>(output),
nullptr
};
const int code = circom_main(3, argv);
if (code == 0) {
return status_ok();
}
const std::string message = "Witness generation [circom main()] failed with code: " + std::to_string(code) + ".";
return status_new(StatusCode_DynError, message.c_str());
}
extern "C" Status poc_generate_witness_from_files(const char* dat, const char* inputs, const char* output) {
const Status status = validate_generate_witness_from_files_arguments(dat, inputs, output); // NOLINT: if-init
if (status_is_error(status)) {
return status;
}
return exceptions_into_status([&] {
return generate_witness_from_files_impl(dat, inputs, output);
});
}
// ---- Memory-based entry point ----
static Status validate_witness_arguments(const WitnessInput* input, const Bytes* output) {
if (input == nullptr) {
return status_new(StatusCode_InvalidInput, "input is null.");
}
if (input->dat.data == nullptr) {
return status_new(StatusCode_InvalidInput, "input.dat.data is null.");
}
if (input->dat.size == 0) {
return status_new(StatusCode_InvalidInput, "input.dat.size is zero.");
}
if (input->inputs_json == nullptr) {
return status_new(StatusCode_InvalidInput, "input.inputs_json is null.");
}
if (output == nullptr) {
return status_new(StatusCode_InvalidInput, "output is null.");
}
if (output->data != nullptr) {
return status_new(StatusCode_InvalidInput, "output.data is not null.");
}
return status_ok();
}
static Status generate_witness_impl(const WitnessInput* input, Bytes* output) {
const ConstBytes& circuit_bytes = input->dat;
Circom_Circuit* circuit = loadCircuit(circuit_bytes);
Circom_CalcWit* ctx = new Circom_CalcWit(circuit);
loadJson(ctx, input->inputs_json);
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();
}
extern "C" Status poc_generate_witness(const WitnessInput* input, Bytes* output) {
const Status status = validate_witness_arguments(input, output); // NOLINT: if-init
if (status_is_error(status)) {
return status;
}
return exceptions_into_status([&] {
return generate_witness_impl(input, output);
});
}

42
src/poc/ffi.hpp Normal file
View File

@ -0,0 +1,42 @@
#ifndef FFI_POC_HPP
#define FFI_POC_HPP
#include "types.hpp"
#ifdef __cplusplus
extern "C" {
#endif
/// Generates a witness by delegating to the circom-generated CLI entry point.
///
/// # Parameters
///
/// - `dat`: Path to the .dat file. Must be extensionless.
/// - `inputs`: Path to the inputs file for the circuit. Must be a JSON file.
/// - `output`: Path to the output file where the witness will be written.
///
/// # Returns
///
/// On success, returns a `Status` with `StatusCode_Ok` and writes the witness to the specified output file.
/// On failure, returns a `Status` with an appropriate error code.
Status poc_generate_witness_from_files(const char* dat, const char* inputs, const char* output);
/// Generates a witness from in-memory buffers.
///
/// # Parameters
///
/// - `input`: The `WitnessInput` struct containing the circuit information.
/// - `output`: Pointer to a `Bytes` struct that will be populated with the generated witness bytes.
///
/// # Returns
///
/// On success, returns a `Status` with `StatusCode_Ok` and populates `output` with the generated witness bytes. The
/// caller is responsible for freeing the resources allocated into `output` by this function using `free_bytes`.
/// On failure, returns a `Status` with an appropriate error code, and `output` will not be modified.
Status poc_generate_witness(const WitnessInput* input, Bytes* output);
#ifdef __cplusplus
}
#endif
#endif

119
src/pol/ffi.cpp Normal file
View File

@ -0,0 +1,119 @@
#include "pol/ffi.hpp"
#include "circom_fwd.hpp"
#include "circom_adapter.hpp"
#include <string>
#include <algorithm>
#include "../types.hpp"
template<typename T>
static Status exceptions_into_status(T&& func) {
try {
return func();
} catch (const std::bad_alloc&) {
return status_from_code(StatusCode_OutOfMemory);
} catch (const std::exception& e) {
return status_new(StatusCode_DynError, e.what());
} catch (...) {
return status_new(StatusCode_DynError, "An unknown error occurred.");
}
}
static Status validate_generate_witness_from_files_arguments(const char* dat, const char* inputs, const char* output) {
if (dat == nullptr) {
return status_new(StatusCode_InvalidInput, "dat is null.");
}
if (inputs == nullptr) {
return status_new(StatusCode_InvalidInput, "inputs is null.");
}
if (output == nullptr) {
return status_new(StatusCode_InvalidInput, "output is null.");
}
return status_ok();
}
static Status generate_witness_from_files_impl(const char* dat, const char* inputs, const char* output) {
char* argv[] = {
const_cast<char*>(dat),
const_cast<char*>(inputs),
const_cast<char*>(output),
nullptr
};
const int code = circom_main(3, argv);
if (code == 0) {
return status_ok();
}
const std::string message = "Witness generation [circom main()] failed with code: " + std::to_string(code) + ".";
return status_new(StatusCode_DynError, message.c_str());
}
extern "C" Status pol_generate_witness_from_files(const char* dat, const char* inputs, const char* output) {
const Status status = validate_generate_witness_from_files_arguments(dat, inputs, output); // NOLINT: if-init
if (status_is_error(status)) {
return status;
}
return exceptions_into_status([&] {
return generate_witness_from_files_impl(dat, inputs, output);
});
}
// ---- Memory-based entry point ----
static Status validate_witness_arguments(const WitnessInput* input, const Bytes* output) {
if (input == nullptr) {
return status_new(StatusCode_InvalidInput, "input is null.");
}
if (input->dat.data == nullptr) {
return status_new(StatusCode_InvalidInput, "input.dat.data is null.");
}
if (input->dat.size == 0) {
return status_new(StatusCode_InvalidInput, "input.dat.size is zero.");
}
if (input->inputs_json == nullptr) {
return status_new(StatusCode_InvalidInput, "input.inputs_json is null.");
}
if (output == nullptr) {
return status_new(StatusCode_InvalidInput, "output is null.");
}
if (output->data != nullptr) {
return status_new(StatusCode_InvalidInput, "output.data is not null.");
}
return status_ok();
}
static Status generate_witness_impl(const WitnessInput* input, Bytes* output) {
const ConstBytes& circuit_bytes = input->dat;
Circom_Circuit* circuit = loadCircuit(circuit_bytes);
Circom_CalcWit* ctx = new Circom_CalcWit(circuit);
loadJson(ctx, input->inputs_json);
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();
}
extern "C" Status pol_generate_witness(const WitnessInput* input, Bytes* output) {
const Status status = validate_witness_arguments(input, output); // NOLINT: if-init
if (status_is_error(status)) {
return status;
}
return exceptions_into_status([&] {
return generate_witness_impl(input, output);
});
}

42
src/pol/ffi.hpp Normal file
View File

@ -0,0 +1,42 @@
#ifndef FFI_POL_HPP
#define FFI_POL_HPP
#include "types.hpp"
#ifdef __cplusplus
extern "C" {
#endif
/// Generates a witness by delegating to the circom-generated CLI entry point.
///
/// # Parameters
///
/// - `dat`: Path to the .dat file. Must be extensionless.
/// - `inputs`: Path to the inputs file for the circuit. Must be a JSON file.
/// - `output`: Path to the output file where the witness will be written.
///
/// # Returns
///
/// On success, returns a `Status` with `StatusCode_Ok` and writes the witness to the specified output file.
/// On failure, returns a `Status` with an appropriate error code.
Status pol_generate_witness_from_files(const char* dat, const char* inputs, const char* output);
/// Generates a witness from in-memory buffers.
///
/// # Parameters
///
/// - `input`: The `WitnessInput` struct containing the circuit information.
/// - `output`: Pointer to a `Bytes` struct that will be populated with the generated witness bytes.
///
/// # Returns
///
/// On success, returns a `Status` with `StatusCode_Ok` and populates `output` with the generated witness bytes. The
/// caller is responsible for freeing the resources allocated into `output` by this function using `free_bytes`.
/// On failure, returns a `Status` with an appropriate error code, and `output` will not be modified.
Status pol_generate_witness(const WitnessInput* input, Bytes* output);
#ifdef __cplusplus
}
#endif
#endif

119
src/signature/ffi.cpp Normal file
View File

@ -0,0 +1,119 @@
#include "signature/ffi.hpp"
#include "circom_fwd.hpp"
#include "circom_adapter.hpp"
#include <string>
#include <algorithm>
#include "../types.hpp"
template<typename T>
static Status exceptions_into_status(T&& func) {
try {
return func();
} catch (const std::bad_alloc&) {
return status_from_code(StatusCode_OutOfMemory);
} catch (const std::exception& e) {
return status_new(StatusCode_DynError, e.what());
} catch (...) {
return status_new(StatusCode_DynError, "An unknown error occurred.");
}
}
static Status validate_generate_witness_from_files_arguments(const char* dat, const char* inputs, const char* output) {
if (dat == nullptr) {
return status_new(StatusCode_InvalidInput, "dat is null.");
}
if (inputs == nullptr) {
return status_new(StatusCode_InvalidInput, "inputs is null.");
}
if (output == nullptr) {
return status_new(StatusCode_InvalidInput, "output is null.");
}
return status_ok();
}
static Status generate_witness_from_files_impl(const char* dat, const char* inputs, const char* output) {
char* argv[] = {
const_cast<char*>(dat),
const_cast<char*>(inputs),
const_cast<char*>(output),
nullptr
};
const int code = circom_main(3, argv);
if (code == 0) {
return status_ok();
}
const std::string message = "Witness generation [circom main()] failed with code: " + std::to_string(code) + ".";
return status_new(StatusCode_DynError, message.c_str());
}
extern "C" Status signature_generate_witness_from_files(const char* dat, const char* inputs, const char* output) {
const Status status = validate_generate_witness_from_files_arguments(dat, inputs, output); // NOLINT: if-init
if (status_is_error(status)) {
return status;
}
return exceptions_into_status([&] {
return generate_witness_from_files_impl(dat, inputs, output);
});
}
// ---- Memory-based entry point ----
static Status validate_witness_arguments(const WitnessInput* input, const Bytes* output) {
if (input == nullptr) {
return status_new(StatusCode_InvalidInput, "input is null.");
}
if (input->dat.data == nullptr) {
return status_new(StatusCode_InvalidInput, "input.dat.data is null.");
}
if (input->dat.size == 0) {
return status_new(StatusCode_InvalidInput, "input.dat.size is zero.");
}
if (input->inputs_json == nullptr) {
return status_new(StatusCode_InvalidInput, "input.inputs_json is null.");
}
if (output == nullptr) {
return status_new(StatusCode_InvalidInput, "output is null.");
}
if (output->data != nullptr) {
return status_new(StatusCode_InvalidInput, "output.data is not null.");
}
return status_ok();
}
static Status generate_witness_impl(const WitnessInput* input, Bytes* output) {
const ConstBytes& circuit_bytes = input->dat;
Circom_Circuit* circuit = loadCircuit(circuit_bytes);
Circom_CalcWit* ctx = new Circom_CalcWit(circuit);
loadJson(ctx, input->inputs_json);
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();
}
extern "C" Status signature_generate_witness(const WitnessInput* input, Bytes* output) {
const Status status = validate_witness_arguments(input, output); // NOLINT: if-init
if (status_is_error(status)) {
return status;
}
return exceptions_into_status([&] {
return generate_witness_impl(input, output);
});
}

42
src/signature/ffi.hpp Normal file
View File

@ -0,0 +1,42 @@
#ifndef FFI_SIGNATURE_HPP
#define FFI_SIGNATURE_HPP
#include "types.hpp"
#ifdef __cplusplus
extern "C" {
#endif
/// Generates a witness by delegating to the circom-generated CLI entry point.
///
/// # Parameters
///
/// - `dat`: Path to the .dat file. Must be extensionless.
/// - `inputs`: Path to the inputs file for the circuit. Must be a JSON file.
/// - `output`: Path to the output file where the witness will be written.
///
/// # Returns
///
/// On success, returns a `Status` with `StatusCode_Ok` and writes the witness to the specified output file.
/// On failure, returns a `Status` with an appropriate error code.
Status signature_generate_witness_from_files(const char* dat, const char* inputs, const char* output);
/// Generates a witness from in-memory buffers.
///
/// # Parameters
///
/// - `input`: The `WitnessInput` struct containing the circuit information.
/// - `output`: Pointer to a `Bytes` struct that will be populated with the generated witness bytes.
///
/// # Returns
///
/// On success, returns a `Status` with `StatusCode_Ok` and populates `output` with the generated witness bytes. The
/// caller is responsible for freeing the resources allocated into `output` by this function using `free_bytes`.
/// On failure, returns a `Status` with an appropriate error code, and `output` will not be modified.
Status signature_generate_witness(const WitnessInput* input, Bytes* output);
#ifdef __cplusplus
}
#endif
#endif