From d0bb48b0e0fde21c96833bf4f2e153e347759e2c Mon Sep 17 00:00:00 2001 From: Alejandro Cabeza Romero Date: Thu, 9 Apr 2026 17:43:58 +0200 Subject: [PATCH] Add thin wrapper to expose witness generation via FFI. --- .../compile-witness-generator/action.yml | 12 ++++++ .github/resources/witness-generator/Makefile | 14 ++++--- src/ffi.cpp | 39 ++++++++++++++++++ src/ffi.hpp | 40 +++++++++++++++++++ src/types.hpp | 26 ++++++++++++ 5 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 src/ffi.cpp create mode 100644 src/ffi.hpp create mode 100644 src/types.hpp diff --git a/.github/actions/compile-witness-generator/action.yml b/.github/actions/compile-witness-generator/action.yml index 2bddaa1..0d8f5b0 100644 --- a/.github/actions/compile-witness-generator/action.yml +++ b/.github/actions/compile-witness-generator/action.yml @@ -69,6 +69,16 @@ runs: CIRCUIT_FILENAME: ${{ steps.parse-circuit-path.outputs.CIRCUIT_FILENAME }} run: circom --c --r1cs --no_asm --O2 "${CIRCUIT_FILENAME}" + - name: Copy ${{ inputs.circuit-name-display }} FFI Sources + shell: bash + env: + SOURCES_ROOT: ${{ github.workspace }}/src + CIRCUIT_CPP_PATH: ${{ steps.parse-circuit-path.outputs.CIRCUIT_CPP_PATH }} + run: | + cp "${SOURCES_ROOT}/ffi.cpp" "${CIRCUIT_CPP_PATH}/ffi.cpp" + cp "${SOURCES_ROOT}/ffi.hpp" "${CIRCUIT_CPP_PATH}/ffi.hpp" + cp "${SOURCES_ROOT}/types.hpp" "${CIRCUIT_CPP_PATH}/types.hpp" + # TODO: Instead of replace, make a fork that generates the appropriate Makefile - name: Replace ${{ inputs.circuit-name-display }}'s Makefile shell: bash @@ -114,6 +124,8 @@ runs: mv "${CIRCUIT_CPP_PATH}/calcwit.hpp" "${CIRCUIT_CPP_PATH}/include/" mv "${CIRCUIT_CPP_PATH}/circom.hpp" "${CIRCUIT_CPP_PATH}/include/" mv "${CIRCUIT_CPP_PATH}/fr.hpp" "${CIRCUIT_CPP_PATH}/include/" + mv "${CIRCUIT_CPP_PATH}/ffi.hpp" "${CIRCUIT_CPP_PATH}/include/" + mv "${CIRCUIT_CPP_PATH}/types.hpp" "${CIRCUIT_CPP_PATH}/include/" - name: Upload ${{ inputs.circuit-name-display }} uses: actions/upload-artifact@de65e23aa2b7e23d713bb51fbfcb6d502f8667d8 diff --git a/.github/resources/witness-generator/Makefile b/.github/resources/witness-generator/Makefile index 95dcb72..35989e0 100644 --- a/.github/resources/witness-generator/Makefile +++ b/.github/resources/witness-generator/Makefile @@ -20,11 +20,13 @@ else # On non-macOS targets we assume objcopy is available on PATH. OBJCOPY := objcopy endif -SRCS := main.cpp calcwit.cpp fr.cpp $(PROJECT).cpp -OBJS := $(SRCS:.cpp=.o) -LIB_SRCS := calcwit.cpp fr.cpp $(PROJECT).cpp +COMMON_SRCS := main.cpp calcwit.cpp fr.cpp $(PROJECT).cpp +COMMON_OBJS := $(COMMON_SRCS:.cpp=.o) +LIB_ONLY_SRCS := ffi.cpp +LIB_ONLY_OBJS := $(LIB_ONLY_SRCS:.cpp=.o) +LIB_SRCS := $(COMMON_SRCS) $(LIB_ONLY_SRCS) LIB_OBJS := $(LIB_SRCS:.cpp=.o) -DEPS_HPP := circom.hpp calcwit.hpp fr.hpp +DEPS_HPP := circom.hpp calcwit.hpp fr.hpp types.hpp ffi.hpp BIN := $(PROJECT) ifeq ($(OS),windows) LIB_EXT := .lib @@ -61,7 +63,7 @@ windows-lib: CXXFLAGS=$(CXXFLAGS_COMMON) -fPIC -I/include -Duint="unsigned int" windows-lib: $(LIB) # ---- Rules ---- -$(BIN): $(OBJS) +$(BIN): $(COMMON_OBJS) $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ $(LIB): $(LIB_OBJS) @@ -72,4 +74,4 @@ $(LIB): $(LIB_OBJS) $(CXX) $(CXXFLAGS) -c $< -o $@ clean: - rm -f $(OBJS) $(LIB_OBJS) $(BIN) $(LIB) + rm -f $(COMMON_OBJS) $(LIB_ONLY_OBJS) $(BIN) $(LIB) diff --git a/src/ffi.cpp b/src/ffi.cpp new file mode 100644 index 0000000..014afcd --- /dev/null +++ b/src/ffi.cpp @@ -0,0 +1,39 @@ +#include "ffi.hpp" + +#include +#include + +#include + +#include "calcwit.hpp" +#include "circom.hpp" +#include "fr.hpp" + +using json = nlohmann::json; + +// ---- Forward declarations from circom-generated main.cpp ---- + +bool check_valid_number(std::string& s, uint base); +void json2FrElements(json val, std::vector& vval); +json::value_t check_type(std::string prefix, json in); +void qualify_input(std::string prefix, json& in, json& in1); +void qualify_input_list(std::string prefix, json& in, json& in1); + +// ---- File-based entry point (wraps circom-generated main) ---- + +extern "C" int generate_witness_from_files(const char* dat, const char* inputs, const char* output) { + char* argv[] = { + (char*)dat, + (char*)inputs, + (char*)output, + nullptr + }; + return main(3, argv); +} + +// ---- Memory-based entry point ---- + +extern "C" int generate_witness(const WitnessInput input, Bytes* output) { + // TODO + return 0; +} diff --git a/src/ffi.hpp b/src/ffi.hpp new file mode 100644 index 0000000..ec29ece --- /dev/null +++ b/src/ffi.hpp @@ -0,0 +1,40 @@ +#ifndef FFI_HPP +#define FFI_HPP + +#include "types.hpp" + +/// Inputs for witness generation. +struct WitnessInput { + /// Contents of the circuit's .dat file. + const ConstBytes dat; + /// Null-terminated JSON string of circuit inputs. + const char* inputs_json; +}; + +#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 where the output witness file will be written. +int 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`: On success, this will be populated with the generated witness bytes. +/// The caller is responsible for freeing this buffer. +int generate_witness(const WitnessInput input, Bytes* output); + +#ifdef __cplusplus +} +#endif + +#endif // FFI_HPP diff --git a/src/types.hpp b/src/types.hpp new file mode 100644 index 0000000..17a0e99 --- /dev/null +++ b/src/types.hpp @@ -0,0 +1,26 @@ +#ifndef TYPES_HPP +#define TYPES_HPP + +#include +#include + +/// A pointer to a contiguous sequence of elements with a known length. +/// +/// # Parameters +/// +/// - `T`: The element type. Use `const T` for immutable data. +template +struct Slice { + /// Pointer to the first element. + T* data; + /// Number of elements. + size_t size; +}; + +/// Mutable byte buffer. +using Bytes = Slice; + +/// Immutable byte buffer. +using ConstBytes = Slice; + +#endif // TYPES_HPP