Made API fully C-ABI-compatible.

This commit is contained in:
Alejandro Cabeza Romero 2026-04-10 16:22:33 +02:00
parent 5946705b1c
commit dbb28502bd
No known key found for this signature in database
GPG Key ID: DA3D14AE478030FD
3 changed files with 119 additions and 85 deletions

View File

@ -24,83 +24,118 @@ void qualify_input_list(std::string prefix, json& in, json& in1);
// -------------------------------------------------------------
// ---- File-based entry point (wraps circom-generated main) ----
static status::Status validate_generate_witness_from_files_input(const char* dat, const char* inputs, const char* output) {
if (dat == nullptr) {
return status::Status{status::StatusCode::InvalidInput, "dat is null"};
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{StatusCode_DynError, e.what()};
} catch (...) {
return Status{StatusCode_DynError, "An unknown error occurred."};
}
if (inputs == nullptr) {
return status::Status{status::StatusCode::InvalidInput, "inputs is null"};
}
if (output == nullptr) {
return status::Status{status::StatusCode::InvalidInput, "output is null"};
}
return status::ok();
}
extern "C" status::Status generate_witness_from_files(const char* dat, const char* inputs, const char* output) {
const status::Status status = validate_generate_witness_from_files_input(dat, inputs, output);
if (is_error(status)) {
return status;
static Status validate_generate_witness_from_files_input(const char* dat, const char* inputs, const char* output) {
if (dat == nullptr) {
return Status{StatusCode_InvalidInput, "dat is null"};
}
if (inputs == nullptr) {
return Status{StatusCode_InvalidInput, "inputs is null"};
}
if (output == nullptr) {
return Status{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*>("generate_witness_from_files"),
const_cast<char*>(dat),
const_cast<char*>(inputs),
const_cast<char*>(output),
nullptr
};
const int code = main(3, argv);
const int code = main(4, argv);
if (code == 0) {
return status::ok();
return status_ok();
}
return status::from_code(status::StatusCode::DynError);
return status_from_code(StatusCode_DynError);
}
extern "C" Status generate_witness_from_files(const char* dat, const char* inputs, const char* output) {
const Status status = validate_generate_witness_from_files_input(dat, inputs, output);
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::Status validate_witness_input(const WitnessInput* input, const Bytes* output) {
static Status validate_witness_input(const WitnessInput* input, const Bytes* output) {
if (output == nullptr) {
return status::Status{status::StatusCode::InvalidInput, "output is null"};
return Status{StatusCode_InvalidInput, "output is null"};
}
if (output->data != nullptr) {
return status::Status{status::StatusCode::InvalidInput, "output.data is not null"};
return Status{StatusCode_InvalidInput, "output.data is not null"};
}
if (input == nullptr) {
return status::Status{status::StatusCode::InvalidInput, "input is null"};
return Status{StatusCode_InvalidInput, "input is null"};
}
if (input->dat.data == nullptr) {
return status::Status{status::StatusCode::InvalidInput, "input.dat.data is null"};
return Status{StatusCode_InvalidInput, "input.dat.data is null"};
}
if (input->dat.size == 0) {
return status::Status{status::StatusCode::InvalidInput, "input.dat.size is zero"};
return Status{StatusCode_InvalidInput, "input.dat.size is zero"};
}
if (input->inputs_json == nullptr) {
return status::Status{status::StatusCode::InvalidInput, "input.inputs_json is null"};
return Status{StatusCode_InvalidInput, "input.inputs_json is null"};
}
return status::ok();
return status_ok();
}
extern "C" status::Status generate_witness(const WitnessInput* input, Bytes* output) {
const status::Status status = validate_witness_input(input, output);
if (is_error(status)) {
return status;
}
static Status generate_witness_impl(const WitnessInput* input, Bytes* output) {
// TODO: Implement the actual witness generation logic using the provided input data.
const uint8_t dummy_witness[] = {0, 1, 2, 3}; // Placeholder for actual witness data
const size_t witness_size = sizeof(dummy_witness);
uint8_t* witness_data = static_cast<uint8_t*>(malloc(witness_size));
if (witness_data == nullptr) {
return status::Status{status::StatusCode::OutOfMemory, "Failed to allocate witness memory"};
return Status{StatusCode_OutOfMemory, "Failed to allocate witness memory"};
}
std::copy(dummy_witness, dummy_witness + witness_size, witness_data);
output->data = witness_data;
output->size = witness_size;
return status::ok();
return status_ok();
}
extern "C" Status generate_witness(const WitnessInput* input, Bytes* output) {
const Status status = validate_witness_input(input, output);
if (status_is_error(status)) {
return status;
}
return exceptions_into_status([&] {
return generate_witness_impl(input, output);
});
}
extern "C" void free_bytes(Bytes* bytes) {
if (bytes == nullptr) {
return;
}
free(bytes->data);
bytes->data = nullptr;
bytes->size = 0;
}

View File

@ -4,12 +4,12 @@
#include "types.hpp"
/// Inputs for witness generation.
struct WitnessInput {
typedef struct WitnessInput {
/// Contents of the circuit's .dat file.
const ConstBytes dat;
/// Null-terminated JSON string of circuit inputs.
const char* inputs_json;
};
} WitnessInput;
#ifdef __cplusplus
extern "C" {
@ -25,9 +25,9 @@ extern "C" {
///
/// # Returns
///
/// On success, returns a `Status` with `StatusCode::Ok` and writes the witness to the specified output file.
/// 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::Status generate_witness_from_files(const char* dat, const char* inputs, const char* output);
Status generate_witness_from_files(const char* dat, const char* inputs, const char* output);
/// Generates a witness from in-memory buffers.
///
@ -38,10 +38,12 @@ status::Status generate_witness_from_files(const char* dat, const char* inputs,
///
/// # Returns
///
/// On success, returns a `Status` with `StatusCode::Ok` and populates `output` with the generated witness bytes. The
/// caller is responsible for freeing the memory allocated for `output.data`.
/// 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::Status generate_witness(const WitnessInput* input, Bytes* output);
Status generate_witness(const WitnessInput* input, Bytes* output);
void free_bytes(Bytes* bytes);
#ifdef __cplusplus
}

View File

@ -1,57 +1,54 @@
#ifndef TYPES_HPP
#define TYPES_HPP
#include <cstddef>
#include <cstdint>
#include <stddef.h>
#include <stdint.h>
/// A pointer to a contiguous sequence of elements with a known length.
///
/// # Parameters
///
/// - `T`: The element type. Use `const T` for immutable data.
template<typename T>
struct Slice {
/// Pointer to the first element.
T* data;
/// Number of elements.
size_t size;
};
#ifdef __cplusplus
extern "C" {
#endif
/// Mutable byte buffer.
using Bytes = Slice<uint8_t>;
typedef struct Bytes {
uint8_t* data;
size_t size;
} Bytes;
/// Immutable byte buffer.
using ConstBytes = Slice<const uint8_t>;
typedef struct ConstBytes {
const uint8_t* data;
size_t size;
} ConstBytes;
namespace status {
/// Represents the outcome of an operation.
enum StatusCode {
Ok = 0,
DynError = 1,
InvalidInput = 2,
OutOfMemory = 3,
};
inline bool is_ok(const StatusCode code) {
return code == Ok;
}
inline bool is_error(const StatusCode code) {
return !is_ok(code);
}
/// A status code with an optional human-readable description.
struct Status {
StatusCode code;
const char* message;
};
inline Status from_code(const StatusCode code) { return Status { code, nullptr }; }
inline Status ok() { return from_code(Ok); }
inline bool is_ok(const Status status) { return is_ok(status.code); }
inline bool is_error(const Status status) { return is_error(status.code); }
typedef enum StatusCode {
StatusCode_Ok = 0,
StatusCode_DynError = 1,
StatusCode_InvalidInput = 2,
StatusCode_OutOfMemory = 3,
} StatusCode;
static bool status_code_is_ok(const StatusCode code) {
return code == StatusCode_Ok;
}
static bool status_code_is_error(const StatusCode code) {
return !status_code_is_ok(code);
}
/// A status code with an optional human-readable description.
typedef struct Status {
StatusCode code;
const char* message;
} Status;
static Status status_from_code(const StatusCode code) { return Status { code, NULL }; }
static Status status_ok() { return status_from_code(StatusCode_Ok); }
static bool status_is_ok(const Status status) { return status_code_is_ok(status.code); }
static bool status_is_error(const Status status) { return status_code_is_error(status.code); }
#ifdef __cplusplus
}
#endif
#endif // TYPES_HPP