feat: Circuits as libraries (#16)

This commit is contained in:
Álex 2026-05-14 15:03:27 +02:00 committed by GitHub
parent d6cf41f665
commit 64ec496067
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
73 changed files with 4281 additions and 186 deletions

34
.cargo-deny.toml Normal file
View File

@ -0,0 +1,34 @@
# Config file reference can be found at https://embarkstudios.github.io/cargo-deny/checks/cfg.html.
[graph]
all-features = true
exclude-dev = true
no-default-features = true
[advisories]
ignore = []
unused-ignored-advisory = "deny"
yanked = "deny"
[bans]
allow-wildcard-paths = false
multiple-versions = "allow"
[licenses]
allow = [
"Apache-2.0 WITH LLVM-exception",
"Apache-2.0",
"BSD-3-Clause",
"CDLA-Permissive-2.0",
"ISC",
"MIT",
"MPL-2.0",
"Unicode-3.0",
"Zlib",
]
private = { ignore = false }
unused-allowed-license = "deny"
[sources]
unknown-git = "deny"
unknown-registry = "deny"

View File

@ -9,7 +9,7 @@ inputs:
description: "The name of the Circom circuit to compile."
required: true
circuit-name-binary:
description: "The final name of the compiled binary. The name should be extensionless."
description: "The name used for artifact labelling. Should match the circuit's canonical name (e.g. pol, poq)."
required: true
circuit-path:
description: "The path to the Circom circuit file relative to the repository root."
@ -45,10 +45,12 @@ runs:
CIRCUIT_FILENAME="$(basename ${CIRCUIT_PATH})"
CIRCUIT_FILESTEM="${CIRCUIT_FILENAME%.circom}"
CIRCUIT_CPP_DIRNAME="${CIRCUIT_FILESTEM}_cpp"
compiled_binary_name="${CIRCUIT_FILESTEM}"
if [ "${OS}" = "windows" ]; then
compiled_binary_name="${compiled_binary_name}.exe"
LIB_EXT=".lib"
LIB_PREFIX=""
else
LIB_EXT=".a"
LIB_PREFIX="lib"
fi
{
@ -59,7 +61,7 @@ runs:
echo "CIRCUIT_CPP_PATH=${CIRCUIT_DIRECTORY}/${CIRCUIT_CPP_DIRNAME}"
echo "WITNESS_GENERATOR_RESOURCES_PATH=${RESOURCES_PATH}/witness-generator"
echo "BUNDLE_TRIPLET=${BUNDLE_TRIPLET}"
echo "COMPILED_BINARY_NAME=${compiled_binary_name}"
echo "LIB_NAME=${LIB_PREFIX}${CIRCUIT_FILESTEM}${LIB_EXT}"
} >> "${GITHUB_OUTPUT}"
- name: Generate ${{ inputs.circuit-name-display }}
@ -69,6 +71,19 @@ 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 }}
CIRCUIT_FILESTEM: ${{ steps.parse-circuit-path.outputs.CIRCUIT_FILESTEM }}
run: |
cp -r "${SOURCES_ROOT}/${CIRCUIT_FILESTEM}" "${CIRCUIT_CPP_PATH}/${CIRCUIT_FILESTEM}"
cp "${SOURCES_ROOT}/circom_adapter.cpp" "${CIRCUIT_CPP_PATH}/circom_adapter.cpp"
cp "${SOURCES_ROOT}/circom_adapter.hpp" "${CIRCUIT_CPP_PATH}/circom_adapter.hpp"
cp "${SOURCES_ROOT}/circom_fwd.hpp" "${CIRCUIT_CPP_PATH}/circom_fwd.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
@ -77,6 +92,17 @@ runs:
CIRCUIT_CPP_PATH: ${{ steps.parse-circuit-path.outputs.CIRCUIT_CPP_PATH }}
run: cp "${WITNESS_GENERATOR_RESOURCES_PATH}/Makefile" "${CIRCUIT_CPP_PATH}/Makefile"
# circom-generated main() has no return on the success path; patch it before -O3 turns it into an infinite loop
- name: Patch ${{ inputs.circuit-name-display }} Missing Return
shell: bash
env:
CIRCUIT_CPP_PATH: ${{ steps.parse-circuit-path.outputs.CIRCUIT_CPP_PATH }}
OS: ${{ inputs.os }}
run: |
SED_I="sed -i"
if [ "$OS" = "macos" ]; then SED_I="sed -i ''"; fi
$SED_I ':a;N;$!ba;s/\n}\n\n*$/\n return 0;\n}/' "${CIRCUIT_CPP_PATH}/main.cpp"
# TODO: Instead of insertion, make a fork that includes the appropriate patch (or the actual fix)
- name: Patch MacOS GMP
shell: bash
@ -94,7 +120,7 @@ runs:
env:
CIRCUIT_FILESTEM: ${{ steps.parse-circuit-path.outputs.CIRCUIT_FILESTEM }}
OS: ${{ inputs.os }}
run: make PROJECT="${CIRCUIT_FILESTEM}" "${OS}"
run: make PROJECT="${CIRCUIT_FILESTEM}" "${OS}-lib"
- name: Compile ${{ inputs.circuit-name-display }}
if: ${{ inputs.os == 'windows' }}
@ -103,12 +129,26 @@ runs:
env:
CIRCUIT_FILESTEM: ${{ steps.parse-circuit-path.outputs.CIRCUIT_FILESTEM }}
OS: ${{ inputs.os }}
run: make PROJECT="${CIRCUIT_FILESTEM}" "${OS}"
run: make PROJECT="${CIRCUIT_FILESTEM}" "${OS}-lib"
- name: Stage ${{ inputs.circuit-name-display }} Headers
shell: bash
env:
CIRCUIT_CPP_PATH: ${{ steps.parse-circuit-path.outputs.CIRCUIT_CPP_PATH }}
CIRCUIT_FILESTEM: ${{ steps.parse-circuit-path.outputs.CIRCUIT_FILESTEM }}
run: |
mkdir -p "${CIRCUIT_CPP_PATH}/include"
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}/${CIRCUIT_FILESTEM}/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
with:
name: ${{ inputs.circuit-name-binary }}-${{ inputs.version }}-${{ inputs.os }}-${{ inputs.arch }}
path: |
${{ steps.parse-circuit-path.outputs.CIRCUIT_CPP_PATH }}/${{ steps.parse-circuit-path.outputs.COMPILED_BINARY_NAME }}
${{ steps.parse-circuit-path.outputs.CIRCUIT_CPP_PATH }}/${{ steps.parse-circuit-path.outputs.LIB_NAME }}
${{ steps.parse-circuit-path.outputs.CIRCUIT_CPP_PATH }}/${{ steps.parse-circuit-path.outputs.CIRCUIT_FILESTEM }}.dat
${{ steps.parse-circuit-path.outputs.CIRCUIT_CPP_PATH }}/include

View File

@ -1,4 +1,4 @@
.PHONY: linux macos windows build clean
.PHONY: linux macos windows linux-lib macos-lib windows-lib build clean
# ---- Arguments ----
ifndef PROJECT
@ -7,11 +7,23 @@ endif
# ---- Common ----
CXX := g++
CXXFLAGS_COMMON := -std=c++11 -O3 -I. -Wno-address-of-packed-member
SRCS := main.cpp calcwit.cpp fr.cpp $(PROJECT).cpp
OBJS := $(SRCS:.cpp=.o)
DEPS_HPP := circom.hpp calcwit.hpp fr.hpp
CXXFLAGS_COMMON := -std=c++11 -O3 -I. -Wno-address-of-packed-member -Dmain=circom_main
COMMON_SRCS := main.cpp calcwit.cpp fr.cpp $(PROJECT).cpp
COMMON_OBJS := $(COMMON_SRCS:.cpp=.o)
LIB_ONLY_SRCS := $(PROJECT)/ffi.cpp circom_adapter.cpp
LIB_ONLY_OBJS := $(LIB_ONLY_SRCS:.cpp=.o)
LIB_SRCS := $(COMMON_SRCS) $(LIB_ONLY_SRCS)
LIB_OBJS := $(COMMON_OBJS) $(LIB_ONLY_OBJS)
DEPS_HPP := circom.hpp calcwit.hpp fr.hpp types.hpp $(PROJECT)/ffi.hpp
BIN := $(PROJECT)
ifeq ($(OS),windows)
LIB_EXT := .lib
LIB_PREFIX :=
else
LIB_EXT := .a
LIB_PREFIX := lib
endif
LIB := $(LIB_PREFIX)$(PROJECT)$(LIB_EXT)
# ---- Linux (x86_64 and aarch64) ----
linux: CXXFLAGS=$(CXXFLAGS_COMMON)
@ -19,25 +31,36 @@ linux: LDFLAGS=-static
linux: LDLIBS=-lgmp
linux: $(BIN)
linux-lib: CXXFLAGS=$(CXXFLAGS_COMMON) -fPIC
linux-lib: $(LIB)
# ---- macOS ----
macos: CXXFLAGS=$(CXXFLAGS_COMMON) -I/opt/homebrew/include -include gmp_patch.hpp
macos: LDFLAGS=-Wl,-search_paths_first -Wl,-dead_strip
macos: LDLIBS=/opt/homebrew/lib/libgmp.a
macos: $(BIN)
macos-lib: CXXFLAGS=$(CXXFLAGS_COMMON) -fPIC -I/opt/homebrew/include -include gmp_patch.hpp
macos-lib: $(LIB)
# ---- Windows (MinGW) ----
windows: CXXFLAGS=$(CXXFLAGS_COMMON) -I/include -Duint="unsigned int"
windows: LDFLAGS=-static
windows: LDLIBS=-L/lib -lgmp -lmman
windows: $(BIN)
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)
ar rcs $@ $^
%.o: %.cpp $(DEPS_HPP)
$(CXX) $(CXXFLAGS) -c $< -o $@
clean:
rm -f $(OBJS) $(BIN)
rm -f $(COMMON_OBJS) $(LIB_ONLY_OBJS) $(BIN) $(LIB)

View File

@ -1,4 +1,4 @@
name: Build Circuits
name: CI
on:
push:
@ -13,6 +13,9 @@ on:
description: "Tag to release. Must follow the format of 'vX.Y.Z'."
required: true
env:
CIRCOM_TAG: v2.2.2
jobs:
setup:
name: Configure Environment
@ -49,6 +52,27 @@ jobs:
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Checkout
uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2 # v4.2.2
with:
sparse-checkout: rust/Cargo.toml
sparse-checkout-cone-mode: false
- name: Verify Cargo version
env:
VERSION: ${{ steps.define-version.outputs.version }}
run: |
if [[ "$VERSION" =~ ^v0\.0\.0-pr ]]; then
echo "Skipping version check for PR build."
exit 0
fi
CARGO_VERSION=$(grep '^version = ' rust/Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
if [ "$CARGO_VERSION" != "${VERSION#v}" ]; then
echo "Version mismatch: Cargo.toml has '$CARGO_VERSION' but tag is '$VERSION'."
exit 1
fi
echo "Version check passed: $CARGO_VERSION"
generate-proving-keys:
name: Generate ${{ matrix.circuit.display }} Proving Key
runs-on: ubuntu-latest
@ -66,8 +90,8 @@ jobs:
display: PoQ
file: poq.circom
dir: blend
- name: zksign
display: ZKSign
- name: signature
display: Signature
file: signature.circom
dir: mantle
- name: poc
@ -79,33 +103,30 @@ jobs:
PTAU_URL: "https://storage.googleapis.com/zkevm/ptau/powersOfTau28_hez_final_17.ptau"
PTAU_FILE: "powersOfTau28_hez_final_17.ptau"
steps:
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@fb51252c7ba57d633bc668f941da052e410add48
with:
toolchain: stable
cache: false
- name: Install Circom
run: |
git clone https://github.com/iden3/circom.git
cd circom
RUSTFLAGS="-A dead_code" cargo build --release
RUSTFLAGS="-A dead_code" cargo install --path circom
circom --version
- name: Checkout
uses: actions/checkout@8edcb1bdb4e267140fa742c62e395cd74f332709
- name: Initialise Submodules
run: git submodule update --init --recursive
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0
- name: Install Circom
run: |
git clone --branch ${{ env.CIRCOM_TAG }} --depth 1 https://github.com/iden3/circom.git
cd circom
RUSTFLAGS="-A dead_code" cargo build --release
RUSTFLAGS="-A dead_code" cargo install --path circom
circom --version
- name: Setup Node.js
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b
with:
node-version: '20'
- name: Install snarkjs
run: npm install -g snarkjs@latest
run: npm install -g snarkjs@0.7.6
- name: Cache Powers of Tau
id: cache-ptau
@ -126,6 +147,7 @@ jobs:
cd ${{ matrix.circuit.dir }}
circom --r1cs --O2 ${{ matrix.circuit.file }}
snarkjs groth16 setup ${CIRCUIT_NAME}.r1cs ../${{ env.PTAU_FILE }} ${{ matrix.circuit.name }}-0.zkey
# Single-contributor trusted setup: A proper MPC ceremony is required before production (see issue #17).
head -c 32 /dev/urandom | xxd -p -c 256 | snarkjs zkey contribute ${{ matrix.circuit.name }}-0.zkey ${{ matrix.circuit.name }}.zkey --name="RELEASE" -v
snarkjs zkey export verificationkey ${{ matrix.circuit.name }}.zkey ${{ matrix.circuit.name }}_verification_key.json
env:
@ -140,7 +162,7 @@ jobs:
${{ matrix.circuit.dir }}/${{ matrix.circuit.name }}_verification_key.json
build-linux:
name: Build Linux Binaries (Native)
name: Build Linux Libraries (Native)
runs-on: ubuntu-latest
needs:
- setup
@ -150,26 +172,23 @@ jobs:
OS: linux
ARCH: x86_64
steps:
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@fb51252c7ba57d633bc668f941da052e410add48
with:
toolchain: stable
cache: false
- name: Install Circom
run: |
git clone https://github.com/iden3/circom.git
cd circom
RUSTFLAGS="-A dead_code" cargo build --release
RUSTFLAGS="-A dead_code" cargo install --path circom
circom --version
- name: Checkout
uses: actions/checkout@8edcb1bdb4e267140fa742c62e395cd74f332709
- name: Initialise Submodules
run: git submodule update --init --recursive
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0
- name: Install Circom
run: |
git clone --branch ${{ env.CIRCOM_TAG }} --depth 1 https://github.com/iden3/circom.git
cd circom
RUSTFLAGS="-A dead_code" cargo build --release
RUSTFLAGS="-A dead_code" cargo install --path circom
circom --version
- name: Setup Dependencies
working-directory: rapidsnark
run: sudo apt update -y
@ -262,11 +281,11 @@ jobs:
os: ${{ env.OS }}
arch: ${{ env.ARCH }}
- name: Compile ZKSign Witness Generator
- name: Compile Signature Witness Generator
uses: ./.github/actions/compile-witness-generator
with:
circuit-name-display: "ZKSign"
circuit-name-binary: "zksign"
circuit-name-display: "Signature"
circuit-name-binary: "signature"
circuit-path: "mantle/signature.circom"
version: ${{ env.VERSION }}
os: ${{ env.OS }}
@ -294,11 +313,11 @@ jobs:
name: poq-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
path: witness-generators/poq-artifact
- name: Download ZKSign Witness Generator
- name: Download Signature Witness Generator
uses: actions/download-artifact@448e3f862ab3ef47aa50ff917776823c9946035b
with:
name: zksign-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
path: witness-generators/zksign-artifact
name: signature-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
path: witness-generators/signature-artifact
- name: Download PoC Witness Generator
uses: actions/download-artifact@448e3f862ab3ef47aa50ff917776823c9946035b
@ -329,7 +348,7 @@ jobs:
BUNDLE_NAME: logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
run: |
# Create the bundle directory structure
mkdir -p "${BUNDLE_NAME}"/{pol,poq,zksign,poc}
mkdir -p "${BUNDLE_NAME}"/{pol,poq,signature,poc}
# Create VERSION file
echo "${{ env.VERSION }}" > "${BUNDLE_NAME}/VERSION"
@ -342,29 +361,27 @@ jobs:
chmod +x "${BUNDLE_NAME}/prover"
chmod +x "${BUNDLE_NAME}/verifier"
# Move witness generators into their respective circuit directories
mv witness-generators/pol-artifact/pol "${BUNDLE_NAME}/pol/witness_generator"
# Move witness libraries into their respective circuit directories
mv witness-generators/pol-artifact/libpol.a "${BUNDLE_NAME}/pol/"
mv witness-generators/pol-artifact/pol.dat "${BUNDLE_NAME}/pol/witness_generator.dat"
mv witness-generators/poq-artifact/poq "${BUNDLE_NAME}/poq/witness_generator"
cp -r witness-generators/pol-artifact/include "${BUNDLE_NAME}/pol/"
mv witness-generators/poq-artifact/libpoq.a "${BUNDLE_NAME}/poq/"
mv witness-generators/poq-artifact/poq.dat "${BUNDLE_NAME}/poq/witness_generator.dat"
mv witness-generators/zksign-artifact/signature "${BUNDLE_NAME}/zksign/witness_generator"
mv witness-generators/zksign-artifact/signature.dat "${BUNDLE_NAME}/zksign/witness_generator.dat"
mv witness-generators/poc-artifact/poc "${BUNDLE_NAME}/poc/witness_generator"
cp -r witness-generators/poq-artifact/include "${BUNDLE_NAME}/poq/"
mv witness-generators/signature-artifact/libsignature.a "${BUNDLE_NAME}/signature/"
mv witness-generators/signature-artifact/signature.dat "${BUNDLE_NAME}/signature/witness_generator.dat"
cp -r witness-generators/signature-artifact/include "${BUNDLE_NAME}/signature/"
mv witness-generators/poc-artifact/libpoc.a "${BUNDLE_NAME}/poc/"
mv witness-generators/poc-artifact/poc.dat "${BUNDLE_NAME}/poc/witness_generator.dat"
# Restore execute permissions on witness generators
chmod +x "${BUNDLE_NAME}/pol/witness_generator"
chmod +x "${BUNDLE_NAME}/poq/witness_generator"
chmod +x "${BUNDLE_NAME}/zksign/witness_generator"
chmod +x "${BUNDLE_NAME}/poc/witness_generator"
cp -r witness-generators/poc-artifact/include "${BUNDLE_NAME}/poc/"
# Copy proving keys and verification keys into each circuit directory
cp proving-keys/pol-proving-key/pol.zkey "${BUNDLE_NAME}/pol/proving_key.zkey"
cp proving-keys/pol-proving-key/pol_verification_key.json "${BUNDLE_NAME}/pol/verification_key.json"
cp proving-keys/poq-proving-key/poq.zkey "${BUNDLE_NAME}/poq/proving_key.zkey"
cp proving-keys/poq-proving-key/poq_verification_key.json "${BUNDLE_NAME}/poq/verification_key.json"
cp proving-keys/zksign-proving-key/zksign.zkey "${BUNDLE_NAME}/zksign/proving_key.zkey"
cp proving-keys/zksign-proving-key/zksign_verification_key.json "${BUNDLE_NAME}/zksign/verification_key.json"
cp proving-keys/signature-proving-key/signature.zkey "${BUNDLE_NAME}/signature/proving_key.zkey"
cp proving-keys/signature-proving-key/signature_verification_key.json "${BUNDLE_NAME}/signature/verification_key.json"
cp proving-keys/poc-proving-key/poc.zkey "${BUNDLE_NAME}/poc/proving_key.zkey"
cp proving-keys/poc-proving-key/poc_verification_key.json "${BUNDLE_NAME}/poc/verification_key.json"
@ -378,7 +395,7 @@ jobs:
path: logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
build-linux-aarch64:
name: Build Linux aarch64 Binaries (Native for RPI5)
name: Build Linux aarch64 Libraries (Native for RPI5)
runs-on: ubuntu-22.04-arm
needs:
- setup
@ -388,26 +405,23 @@ jobs:
OS: linux
ARCH: aarch64
steps:
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@fb51252c7ba57d633bc668f941da052e410add48
with:
toolchain: stable
cache: false
- name: Install Circom
run: |
git clone https://github.com/iden3/circom.git
cd circom
RUSTFLAGS="-A dead_code" cargo build --release
RUSTFLAGS="-A dead_code" cargo install --path circom
circom --version
- name: Checkout
uses: actions/checkout@8edcb1bdb4e267140fa742c62e395cd74f332709
- name: Initialise Submodules
run: git submodule update --init --recursive
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0
- name: Install Circom
run: |
git clone --branch ${{ env.CIRCOM_TAG }} --depth 1 https://github.com/iden3/circom.git
cd circom
RUSTFLAGS="-A dead_code" cargo build --release
RUSTFLAGS="-A dead_code" cargo install --path circom
circom --version
- name: Setup Dependencies
working-directory: rapidsnark
run: sudo apt update -y
@ -502,11 +516,11 @@ jobs:
os: ${{ env.OS }}
arch: ${{ env.ARCH }}
- name: Compile ZKSign Witness Generator
- name: Compile Signature Witness Generator
uses: ./.github/actions/compile-witness-generator
with:
circuit-name-display: "ZKSign"
circuit-name-binary: "zksign"
circuit-name-display: "Signature"
circuit-name-binary: "signature"
circuit-path: "mantle/signature.circom"
version: ${{ env.VERSION }}
os: ${{ env.OS }}
@ -534,11 +548,11 @@ jobs:
name: poq-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
path: witness-generators/poq-artifact
- name: Download ZKSign Witness Generator
- name: Download Signature Witness Generator
uses: actions/download-artifact@448e3f862ab3ef47aa50ff917776823c9946035b
with:
name: zksign-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
path: witness-generators/zksign-artifact
name: signature-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
path: witness-generators/signature-artifact
- name: Download PoC Witness Generator
uses: actions/download-artifact@448e3f862ab3ef47aa50ff917776823c9946035b
@ -569,7 +583,7 @@ jobs:
BUNDLE_NAME: logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
run: |
# Create the bundle directory structure
mkdir -p "${BUNDLE_NAME}"/{pol,poq,zksign,poc}
mkdir -p "${BUNDLE_NAME}"/{pol,poq,signature,poc}
# Create VERSION file
echo "${{ env.VERSION }}" > "${BUNDLE_NAME}/VERSION"
@ -582,29 +596,27 @@ jobs:
chmod +x "${BUNDLE_NAME}/prover"
chmod +x "${BUNDLE_NAME}/verifier"
# Move witness generators into their respective circuit directories
mv witness-generators/pol-artifact/pol "${BUNDLE_NAME}/pol/witness_generator"
# Move witness libraries into their respective circuit directories
mv witness-generators/pol-artifact/libpol.a "${BUNDLE_NAME}/pol/"
mv witness-generators/pol-artifact/pol.dat "${BUNDLE_NAME}/pol/witness_generator.dat"
mv witness-generators/poq-artifact/poq "${BUNDLE_NAME}/poq/witness_generator"
cp -r witness-generators/pol-artifact/include "${BUNDLE_NAME}/pol/"
mv witness-generators/poq-artifact/libpoq.a "${BUNDLE_NAME}/poq/"
mv witness-generators/poq-artifact/poq.dat "${BUNDLE_NAME}/poq/witness_generator.dat"
mv witness-generators/zksign-artifact/signature "${BUNDLE_NAME}/zksign/witness_generator"
mv witness-generators/zksign-artifact/signature.dat "${BUNDLE_NAME}/zksign/witness_generator.dat"
mv witness-generators/poc-artifact/poc "${BUNDLE_NAME}/poc/witness_generator"
cp -r witness-generators/poq-artifact/include "${BUNDLE_NAME}/poq/"
mv witness-generators/signature-artifact/libsignature.a "${BUNDLE_NAME}/signature/"
mv witness-generators/signature-artifact/signature.dat "${BUNDLE_NAME}/signature/witness_generator.dat"
cp -r witness-generators/signature-artifact/include "${BUNDLE_NAME}/signature/"
mv witness-generators/poc-artifact/libpoc.a "${BUNDLE_NAME}/poc/"
mv witness-generators/poc-artifact/poc.dat "${BUNDLE_NAME}/poc/witness_generator.dat"
# Restore execute permissions on witness generators
chmod +x "${BUNDLE_NAME}/pol/witness_generator"
chmod +x "${BUNDLE_NAME}/poq/witness_generator"
chmod +x "${BUNDLE_NAME}/zksign/witness_generator"
chmod +x "${BUNDLE_NAME}/poc/witness_generator"
cp -r witness-generators/poc-artifact/include "${BUNDLE_NAME}/poc/"
# Copy proving keys and verification keys into each circuit directory
cp proving-keys/pol-proving-key/pol.zkey "${BUNDLE_NAME}/pol/proving_key.zkey"
cp proving-keys/pol-proving-key/pol_verification_key.json "${BUNDLE_NAME}/pol/verification_key.json"
cp proving-keys/poq-proving-key/poq.zkey "${BUNDLE_NAME}/poq/proving_key.zkey"
cp proving-keys/poq-proving-key/poq_verification_key.json "${BUNDLE_NAME}/poq/verification_key.json"
cp proving-keys/zksign-proving-key/zksign.zkey "${BUNDLE_NAME}/zksign/proving_key.zkey"
cp proving-keys/zksign-proving-key/zksign_verification_key.json "${BUNDLE_NAME}/zksign/verification_key.json"
cp proving-keys/signature-proving-key/signature.zkey "${BUNDLE_NAME}/signature/proving_key.zkey"
cp proving-keys/signature-proving-key/signature_verification_key.json "${BUNDLE_NAME}/signature/verification_key.json"
cp proving-keys/poc-proving-key/poc.zkey "${BUNDLE_NAME}/poc/proving_key.zkey"
cp proving-keys/poc-proving-key/poc_verification_key.json "${BUNDLE_NAME}/poc/verification_key.json"
@ -618,7 +630,7 @@ jobs:
path: logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
build-windows:
name: Build Windows Binaries (Native)
name: Build Windows Libraries (Native)
runs-on: windows-latest
needs:
- setup
@ -638,26 +650,23 @@ jobs:
make
git
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@fb51252c7ba57d633bc668f941da052e410add48
with:
toolchain: stable
cache: false
- name: Install Circom
run: |
git clone https://github.com/iden3/circom.git
cd circom
$env:RUSTFLAGS="-A dead_code"; cargo build --release
$env:RUSTFLAGS="-A dead_code"; cargo install --path circom
circom --version
- name: Checkout
uses: actions/checkout@8edcb1bdb4e267140fa742c62e395cd74f332709
- name: Initialise Submodules
run: git submodule update --init --recursive
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0
- name: Install Circom
run: |
git clone --branch ${{ env.CIRCOM_TAG }} --depth 1 https://github.com/iden3/circom.git
cd circom
$env:RUSTFLAGS="-A dead_code"; cargo build --release
$env:RUSTFLAGS="-A dead_code"; cargo install --path circom
circom --version
- name: Install Dependencies [Witness Generator]
shell: msys2 {0}
run: |
@ -780,11 +789,11 @@ jobs:
os: ${{ env.OS }}
arch: ${{ env.ARCH }}
- name: Compile ZKSign Witness Generator
- name: Compile Signature Witness Generator
uses: ./.github/actions/compile-witness-generator
with:
circuit-name-display: "ZKSign"
circuit-name-binary: "zksign"
circuit-name-display: "Signature"
circuit-name-binary: "signature"
circuit-path: "mantle/signature.circom"
version: ${{ env.VERSION }}
os: ${{ env.OS }}
@ -812,11 +821,11 @@ jobs:
name: poq-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
path: witness-generators/poq-artifact
- name: Download ZKSign Witness Generator
- name: Download Signature Witness Generator
uses: actions/download-artifact@448e3f862ab3ef47aa50ff917776823c9946035b
with:
name: zksign-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
path: witness-generators/zksign-artifact
name: signature-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
path: witness-generators/signature-artifact
- name: Download PoC Witness Generator
uses: actions/download-artifact@448e3f862ab3ef47aa50ff917776823c9946035b
@ -848,7 +857,7 @@ jobs:
BUNDLE_NAME: logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
run: |
# Create the bundle directory structure
mkdir -p "${BUNDLE_NAME}"/{pol,poq,zksign,poc}
mkdir -p "${BUNDLE_NAME}"/{pol,poq,signature,poc}
# Create VERSION file
echo "${{ env.VERSION }}" > "${BUNDLE_NAME}/VERSION"
@ -859,23 +868,27 @@ jobs:
mv prover-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}/prover/prover.exe "${BUNDLE_NAME}/prover.exe"
mv verifier-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}/verifier/verifier.exe "${BUNDLE_NAME}/verifier.exe"
# Move witness generators into their respective circuit directories
mv witness-generators/pol-artifact/pol.exe "${BUNDLE_NAME}/pol/witness_generator.exe"
# Move witness libraries into their respective circuit directories
mv witness-generators/pol-artifact/pol.lib "${BUNDLE_NAME}/pol/"
mv witness-generators/pol-artifact/pol.dat "${BUNDLE_NAME}/pol/witness_generator.dat"
mv witness-generators/poq-artifact/poq.exe "${BUNDLE_NAME}/poq/witness_generator.exe"
cp -r witness-generators/pol-artifact/include "${BUNDLE_NAME}/pol/"
mv witness-generators/poq-artifact/poq.lib "${BUNDLE_NAME}/poq/"
mv witness-generators/poq-artifact/poq.dat "${BUNDLE_NAME}/poq/witness_generator.dat"
mv witness-generators/zksign-artifact/signature.exe "${BUNDLE_NAME}/zksign/witness_generator.exe"
mv witness-generators/zksign-artifact/signature.dat "${BUNDLE_NAME}/zksign/witness_generator.dat"
mv witness-generators/poc-artifact/poc.exe "${BUNDLE_NAME}/poc/witness_generator.exe"
cp -r witness-generators/poq-artifact/include "${BUNDLE_NAME}/poq/"
mv witness-generators/signature-artifact/signature.lib "${BUNDLE_NAME}/signature/"
mv witness-generators/signature-artifact/signature.dat "${BUNDLE_NAME}/signature/witness_generator.dat"
cp -r witness-generators/signature-artifact/include "${BUNDLE_NAME}/signature/"
mv witness-generators/poc-artifact/poc.lib "${BUNDLE_NAME}/poc/"
mv witness-generators/poc-artifact/poc.dat "${BUNDLE_NAME}/poc/witness_generator.dat"
cp -r witness-generators/poc-artifact/include "${BUNDLE_NAME}/poc/"
# Copy proving keys and verification keys into each circuit directory
cp proving-keys/pol-proving-key/pol.zkey "${BUNDLE_NAME}/pol/proving_key.zkey"
cp proving-keys/pol-proving-key/pol_verification_key.json "${BUNDLE_NAME}/pol/verification_key.json"
cp proving-keys/poq-proving-key/poq.zkey "${BUNDLE_NAME}/poq/proving_key.zkey"
cp proving-keys/poq-proving-key/poq_verification_key.json "${BUNDLE_NAME}/poq/verification_key.json"
cp proving-keys/zksign-proving-key/zksign.zkey "${BUNDLE_NAME}/zksign/proving_key.zkey"
cp proving-keys/zksign-proving-key/zksign_verification_key.json "${BUNDLE_NAME}/zksign/verification_key.json"
cp proving-keys/signature-proving-key/signature.zkey "${BUNDLE_NAME}/signature/proving_key.zkey"
cp proving-keys/signature-proving-key/signature_verification_key.json "${BUNDLE_NAME}/signature/verification_key.json"
cp proving-keys/poc-proving-key/poc.zkey "${BUNDLE_NAME}/poc/proving_key.zkey"
cp proving-keys/poc-proving-key/poc_verification_key.json "${BUNDLE_NAME}/poc/verification_key.json"
@ -889,7 +902,7 @@ jobs:
path: logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
build-macos:
name: Build MacOS Binaries (Native)
name: Build MacOS Libraries (Native)
runs-on: macos-latest
needs:
- setup
@ -899,31 +912,28 @@ jobs:
ARCH: aarch64
OS: macos
steps:
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@fb51252c7ba57d633bc668f941da052e410add48
with:
toolchain: stable
cache: false
- name: Install Circom
run: |
git clone https://github.com/iden3/circom.git
cd circom
RUSTFLAGS="-A dead_code" cargo build --release
RUSTFLAGS="-A dead_code" cargo install --path circom
circom --version
- name: Checkout
uses: actions/checkout@8edcb1bdb4e267140fa742c62e395cd74f332709
- name: Initialise Submodules
run: git submodule update --init --recursive
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0
- name: Install Circom
run: |
git clone --branch ${{ env.CIRCOM_TAG }} --depth 1 https://github.com/iden3/circom.git
cd circom
RUSTFLAGS="-A dead_code" cargo build --release
RUSTFLAGS="-A dead_code" cargo install --path circom
circom --version
- name: Setup Dependencies
run: mkdir include
- name: Install Dependencies [Witness Generator]
run: brew install nlohmann-json
run: brew install nlohmann-json binutils
- name: Install Dependencies [Prover]
run: brew install nasm m4
@ -1007,11 +1017,11 @@ jobs:
os: ${{ env.OS }}
arch: ${{ env.ARCH }}
- name: Compile ZKSign Witness Generator
- name: Compile Signature Witness Generator
uses: ./.github/actions/compile-witness-generator
with:
circuit-name-display: "ZKSign"
circuit-name-binary: "zksign"
circuit-name-display: "Signature"
circuit-name-binary: "signature"
circuit-path: "mantle/signature.circom"
version: ${{ env.VERSION }}
os: ${{ env.OS }}
@ -1039,11 +1049,11 @@ jobs:
name: poq-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
path: witness-generators/poq-artifact
- name: Download ZKSign Witness Generator
- name: Download Signature Witness Generator
uses: actions/download-artifact@448e3f862ab3ef47aa50ff917776823c9946035b
with:
name: zksign-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
path: witness-generators/zksign-artifact
name: signature-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
path: witness-generators/signature-artifact
- name: Download PoC Witness Generator
uses: actions/download-artifact@448e3f862ab3ef47aa50ff917776823c9946035b
@ -1074,7 +1084,7 @@ jobs:
BUNDLE_NAME: logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}
run: |
# Create the bundle directory structure
mkdir -p "${BUNDLE_NAME}"/{pol,poq,zksign,poc}
mkdir -p "${BUNDLE_NAME}"/{pol,poq,signature,poc}
# Create VERSION file
echo "${{ env.VERSION }}" > "${BUNDLE_NAME}/VERSION"
@ -1087,29 +1097,27 @@ jobs:
chmod +x "${BUNDLE_NAME}/prover"
chmod +x "${BUNDLE_NAME}/verifier"
# Move witness generators into their respective circuit directories
mv witness-generators/pol-artifact/pol "${BUNDLE_NAME}/pol/witness_generator"
# Move witness libraries into their respective circuit directories
mv witness-generators/pol-artifact/libpol.a "${BUNDLE_NAME}/pol/"
mv witness-generators/pol-artifact/pol.dat "${BUNDLE_NAME}/pol/witness_generator.dat"
mv witness-generators/poq-artifact/poq "${BUNDLE_NAME}/poq/witness_generator"
cp -r witness-generators/pol-artifact/include "${BUNDLE_NAME}/pol/"
mv witness-generators/poq-artifact/libpoq.a "${BUNDLE_NAME}/poq/"
mv witness-generators/poq-artifact/poq.dat "${BUNDLE_NAME}/poq/witness_generator.dat"
mv witness-generators/zksign-artifact/signature "${BUNDLE_NAME}/zksign/witness_generator"
mv witness-generators/zksign-artifact/signature.dat "${BUNDLE_NAME}/zksign/witness_generator.dat"
mv witness-generators/poc-artifact/poc "${BUNDLE_NAME}/poc/witness_generator"
cp -r witness-generators/poq-artifact/include "${BUNDLE_NAME}/poq/"
mv witness-generators/signature-artifact/libsignature.a "${BUNDLE_NAME}/signature/"
mv witness-generators/signature-artifact/signature.dat "${BUNDLE_NAME}/signature/witness_generator.dat"
cp -r witness-generators/signature-artifact/include "${BUNDLE_NAME}/signature/"
mv witness-generators/poc-artifact/libpoc.a "${BUNDLE_NAME}/poc/"
mv witness-generators/poc-artifact/poc.dat "${BUNDLE_NAME}/poc/witness_generator.dat"
# Restore execute permissions on witness generators
chmod +x "${BUNDLE_NAME}/pol/witness_generator"
chmod +x "${BUNDLE_NAME}/poq/witness_generator"
chmod +x "${BUNDLE_NAME}/zksign/witness_generator"
chmod +x "${BUNDLE_NAME}/poc/witness_generator"
cp -r witness-generators/poc-artifact/include "${BUNDLE_NAME}/poc/"
# Copy proving keys and verification keys into each circuit directory
cp proving-keys/pol-proving-key/pol.zkey "${BUNDLE_NAME}/pol/proving_key.zkey"
cp proving-keys/pol-proving-key/pol_verification_key.json "${BUNDLE_NAME}/pol/verification_key.json"
cp proving-keys/poq-proving-key/poq.zkey "${BUNDLE_NAME}/poq/proving_key.zkey"
cp proving-keys/poq-proving-key/poq_verification_key.json "${BUNDLE_NAME}/poq/verification_key.json"
cp proving-keys/zksign-proving-key/zksign.zkey "${BUNDLE_NAME}/zksign/proving_key.zkey"
cp proving-keys/zksign-proving-key/zksign_verification_key.json "${BUNDLE_NAME}/zksign/verification_key.json"
cp proving-keys/signature-proving-key/signature.zkey "${BUNDLE_NAME}/signature/proving_key.zkey"
cp proving-keys/signature-proving-key/signature_verification_key.json "${BUNDLE_NAME}/signature/verification_key.json"
cp proving-keys/poc-proving-key/poc.zkey "${BUNDLE_NAME}/poc/proving_key.zkey"
cp proving-keys/poc-proving-key/poc_verification_key.json "${BUNDLE_NAME}/poc/verification_key.json"
@ -1122,6 +1130,119 @@ jobs:
name: logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
path: logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
# The following `Clippy` and `Test` jobs require the library circuits to compile the target repositories.
# To avoid specifying the circuit build job multiple times across the CI, the `Clippy` and `Test` jobs are
# defined in the same workflow as the `Build` job.
# Ideally, if and when the build system is moved to an agnostic tool (such as `cmake`) and that is integrated into
# the sys crates' `build.rs`, these jobs will be moved to their own workflows.
clippy:
name: Clippy
runs-on: ubuntu-latest
needs:
- setup
- build-linux
env:
VERSION: ${{ needs.setup.outputs.version }}
OS: linux
ARCH: x86_64
steps:
- name: Checkout
uses: actions/checkout@8edcb1bdb4e267140fa742c62e395cd74f332709
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0
- name: Install Dependencies
run: sudo apt install -y libgmp-dev
- name: Cache Cargo artifacts
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # Version 4.2.3
with:
path: |
~/.cargo/registry
~/.cargo/git
rust/target
key: ${{ runner.os }}-cargo-${{ hashFiles('rust/Cargo.lock', 'rust/Cargo.toml') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Download Linux Bundle
uses: actions/download-artifact@448e3f862ab3ef47aa50ff917776823c9946035b
with:
name: logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
path: circuit-libs/
- name: Extract Bundle
run: tar -xzf circuit-libs/logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz -C circuit-libs/
- name: Clippy
run: >
cargo clippy
--manifest-path rust/Cargo.toml
--all
--all-targets
--all-features
--
-D warnings
env:
LBC_POC_LIB_DIR: ${{ github.workspace }}/circuit-libs/logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}/poc
LBC_POL_LIB_DIR: ${{ github.workspace }}/circuit-libs/logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}/pol
LBC_POQ_LIB_DIR: ${{ github.workspace }}/circuit-libs/logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}/poq
LBC_SIGNATURE_LIB_DIR: ${{ github.workspace }}/circuit-libs/logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}/signature
# See clippy above — same reason.
test:
name: Test
runs-on: ubuntu-latest
needs:
- setup
- build-linux
env:
VERSION: ${{ needs.setup.outputs.version }}
OS: linux
ARCH: x86_64
steps:
- name: Checkout
uses: actions/checkout@8edcb1bdb4e267140fa742c62e395cd74f332709
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0
- name: Install Dependencies
run: sudo apt install -y libgmp-dev
- name: Cache Cargo artifacts
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # Version 4.2.3
with:
path: |
~/.cargo/registry
~/.cargo/git
rust/target
key: ${{ runner.os }}-cargo-${{ hashFiles('rust/Cargo.lock', 'rust/Cargo.toml') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Download Linux Bundle
uses: actions/download-artifact@448e3f862ab3ef47aa50ff917776823c9946035b
with:
name: logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz
path: circuit-libs/
- name: Extract Bundle
run: tar -xzf circuit-libs/logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}.tar.gz -C circuit-libs/
- name: Test
run: >
cargo test
--manifest-path rust/Cargo.toml
--all
--all-features
env:
LBC_POC_LIB_DIR: ${{ github.workspace }}/circuit-libs/logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}/poc
LBC_POL_LIB_DIR: ${{ github.workspace }}/circuit-libs/logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}/pol
LBC_POQ_LIB_DIR: ${{ github.workspace }}/circuit-libs/logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}/poq
LBC_SIGNATURE_LIB_DIR: ${{ github.workspace }}/circuit-libs/logos-blockchain-circuits-${{ env.VERSION }}-${{ env.OS }}-${{ env.ARCH }}/signature
publish-release:
name: Create Release
runs-on: ubuntu-latest
@ -1134,6 +1255,8 @@ jobs:
- build-linux-aarch64
- build-windows
- build-macos
- clippy
- test
env:
TAG: ${{ needs.setup.outputs.tag }}
VERSION: ${{ needs.setup.outputs.version }}

105
.github/workflows/lint.yml vendored Normal file
View File

@ -0,0 +1,105 @@
name: Lint
on:
push:
branches:
- main
pull_request:
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2 # v4.2.2
# Stable toolchain for building and clippy, version and components from rust-toolchain.toml.
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0
# Nightly toolchain for rustfmt only — nightly is required for formatting features not yet stable.
- name: Install Nightly Toolchain (fmt)
uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0
with:
toolchain: nightly-2026-02-28
components: rustfmt
override: 'false'
- name: Cache Cargo artifacts
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: |
~/.cargo/registry
~/.cargo/git
rust/target
key: ${{ runner.os }}-cargo-${{ hashFiles('rust/Cargo.lock', 'rust/Cargo.toml') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Install taplo
run: cargo install taplo-cli --version 0.9.3 --locked # v0.9.3
- name: Install cargo-deny
run: cargo install cargo-deny --version 0.19.4 --locked # v0.19.4
- name: Install cargo-machete
run: cargo install cargo-machete --version 0.9.2 --locked # v0.9.2
- name: Check Rust formatting
id: fmt
continue-on-error: true
run: cargo +nightly-2026-02-28 fmt --manifest-path rust/Cargo.toml --all -- --check
- name: Audit dependencies
id: deny
continue-on-error: true
run: >
cargo deny
--manifest-path rust/Cargo.toml
--locked
--all-features
check
--hide-inclusion-graph
-c .cargo-deny.toml
--show-stats
-D warnings
- name: Check TOML formatting
id: taplo-fmt
continue-on-error: true
run: taplo fmt --check
- name: Lint TOML
id: taplo-lint
continue-on-error: true
run: taplo lint
- name: Check unused dependencies
id: machete
continue-on-error: true
run: cargo machete rust/
- name: Report
if: always()
run: |
pass="✅" fail="❌"
failed=false
check() {
local name="$1" outcome="$2"
if [ "$outcome" = "success" ]; then
echo "$pass $name"
else
echo "$fail $name ($outcome)"
failed=true
fi
}
check "rustfmt" "${{ steps.fmt.outcome }}"
check "cargo-deny" "${{ steps.deny.outcome }}"
check "taplo fmt" "${{ steps.taplo-fmt.outcome }}"
check "taplo lint" "${{ steps.taplo-lint.outcome }}"
check "cargo-machete" "${{ steps.machete.outcome }}"
$failed && exit 1 || true

16
.gitignore vendored
View File

@ -1,2 +1,18 @@
.idea/
result
*ignore*
# Generated files
*_cpp/
*.r1cs
**/input.json
**/*-input.json
# Rust Codebase
rust/**/target/
# Build
.cache
build
cmake

46
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,46 @@
repos:
- repo: https://github.com/doublify/pre-commit-rust
rev: eeee35a89e69d5772bdee97db1a6a898467b686e # 1.0
hooks:
- id: fmt
entry: cargo +nightly-2026-02-28 fmt --manifest-path rust/Cargo.toml --all
pass_filenames: false
- id: clippy
name: cargo clippy
entry: cargo clippy --manifest-path rust/Cargo.toml
args:
["--all", "--all-targets", "--all-features", "--", "-D", "warnings"]
pass_filenames: false
- repo: https://github.com/EmbarkStudios/cargo-deny
rev: cfe589ec21d70996a3e44d76a8e2b9369f7e0a2f # v0.19.4
hooks:
- id: cargo-deny
args:
- --manifest-path
- rust/Cargo.toml
- --locked
- --all-features
- check
- --hide-inclusion-graph
- -c
- .cargo-deny.toml
- --show-stats
- -D
- warnings
- repo: https://github.com/ComPWA/taplo-pre-commit
rev: ade0f95ddcf661c697d4670d2cfcbe95d0048a0a # v0.9.3 # Can't update until: https://github.com/tamasfe/taplo/issues/805
hooks:
- id: taplo-format
- id: taplo-lint
- repo: https://github.com/bnjbvr/cargo-machete
rev: ac30a525c0a8d163a92d727b3ff079ee3f6ecb08 # v0.9.2
hooks:
- id: cargo-machete
args: ["rust/"]
- repo: local
hooks:
- id: cargo-hack-check
language: script
name: cargo hack check
entry: ./hooks/cargo-hack.sh
stages: [manual]

View File

@ -1,5 +1,42 @@
# Contributor's Guide
## Development Setup
### Prerequisites
- [Rust](https://rustup.rs/) — the pinned toolchain version is in `rust-toolchain.toml` and will be installed automatically by `rustup`.
- [pre-commit](https://pre-commit.com/) — used to run formatting, linting, and audit checks before each commit.
### Installing the Pre-Commit Hooks
```bash
pre-commit install
```
This only needs to be done once after cloning the repo. Hooks will then run automatically on `git commit`.
### Running Checks Manually
To run all hooks manually against all files:
```bash
pre-commit run --all-files
```
### Maintenance
#### Rust Toolchain
When bumping the stable toolchain, update `channel` in `rust-toolchain.toml`. The comment there lists every other place that must be updated in sync (nightly version, CI workflows, pre-commit hooks).
#### Tool Versions
`taplo`, `cargo-deny`, and `cargo-machete` are pinned in two places that must stay in sync:
- `.pre-commit-config.yaml` (hook `rev`)
- `.github/workflows/lint.yml` (`cargo install --version`)
---
## Triggering a New Release for Logos Blockchain Circuits
To trigger a release build:
@ -8,7 +45,8 @@ To trigger a release build:
2. This will automatically trigger the `.github/workflows/build_circuits.yml` workflow.
3. Once the workflow finishes, the generated artifacts will be attached to a new release.
> Currently, releases published this way are marked as **Draft** and **Pre-Release** to ensure that the changelog and pre-release steps are manually reviewed first.
> Currently, releases published this way are marked as **Draft** and **Pre-Release** to ensure that the changelog and
> pre-release steps are manually reviewed first.
### Generated Artifacts
@ -30,34 +68,41 @@ logos-blockchain-circuits-{version}-{os}-{arch}/
├── prover[.exe]
├── verifier[.exe]
├── pol/
│ ├── witness_generator[.exe]
│ ├── libpol.a
│ ├── witness_generator.dat
│ ├── include/
│ ├── proving_key.zkey
│ └── verification_key.json
├── poq/
│ ├── witness_generator[.exe]
│ ├── libpoq.a
│ ├── witness_generator.dat
│ ├── include/
│ ├── proving_key.zkey
│ └── verification_key.json
├── zksign/
│ ├── witness_generator[.exe]
├── signature/
│ ├── libsignature.a
│ ├── witness_generator.dat
│ ├── include/
│ ├── proving_key.zkey
│ └── verification_key.json
└── poc/
├── witness_generator[.exe]
├── libpoc.a
├── witness_generator.dat
├── include/
├── proving_key.zkey
└── verification_key.json
```
> On Windows, static libraries use the `.lib` extension instead of `.a` (e.g. `pol.lib`).
At the root level:
- **prover**: Rapidsnark prover binary for generating zk-SNARK proofs
- **verifier**: Rapidsnark verifier binary for verifying proofs
Each circuit directory contains:
- **witness_generator**: Compiled C++ binary for generating witnesses from inputs
- **lib{circuit}.a / {circuit}.lib**: Static library for generating witnesses from inputs
- **witness_generator.dat**: Required data file for the witness generator
- **include/**: C headers for linking against the witness generator library
- **proving_key.zkey**: Groth16 proving key for generating zk-SNARK proofs
- **verification_key.json**: Verification key for verifying proofs

64
blend/test_ffi.cpp Normal file
View File

@ -0,0 +1,64 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "poq/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 = poq_generate_witness_from_files(
"poq",
"../poq-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("poq.dat", &dat_size);
uint8_t* json_data = read_file("../poq-input.json", &json_size);
WitnessInput input = {
{dat_data, dat_size},
(const char*)json_data
};
Bytes output = {nullptr, 0};
status = poq_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;
}

8
flake.lock generated
View File

@ -2,16 +2,16 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1769461804,
"narHash": "sha256-msG8SU5WsBUfVVa/9RPLaymvi5bI8edTavbIq3vRlhI=",
"lastModified": 1767313136,
"narHash": "sha256-16KkgfdYqjaeRGBaYsNrhPRRENs0qzkQVUooNHtoy2w=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "bfc1b8a4574108ceef22f02bafcf6611380c100d",
"rev": "ac62194c3917d5f474c1a844b6fd6da2db95077d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"ref": "nixos-25.05",
"repo": "nixpkgs",
"type": "github"
}

View File

@ -2,7 +2,7 @@
description = "Logos Blockchain Circuits (GitHub Releases)";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
};
outputs =
@ -19,7 +19,8 @@
forAll = lib.genAttrs systems;
circuitsVersion = "0.4.1"; # TODO: Parametrize or make package per version available
cargoToml = builtins.fromTOML (builtins.readFile ./rust/Cargo.toml);
circuitsVersion = cargoToml.workspace.package.version;
versions = import ./versions.nix;
circuitsHashes = versions.${circuitsVersion};

3
hooks/cargo-hack.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
RUSTFLAGS="-D warnings" cargo hack --feature-powerset --no-dev-deps --keep-going --all-targets check

View File

@ -1,3 +1,21 @@
cargo_root := justfile_directory() + "/rust"
src := justfile_directory() + "/src"
ci_makefile := justfile_directory() + "/.github/resources/witness-generator/Makefile"
circom_version := "2.2.2" # This version must match the version used in the CI
os := `uname -s`
sed_i := if os == "Darwin" { "sed -i ''" } else { "sed -i" }
# Shortcut to run cargo commands from the repo root (the Cargo workspace lives under ./rust/).
# Potentially temporary.
cargo +args:
cd {{cargo_root}} && cargo {{args}}
# Verify the installed circom matches the pinned version.
check-circom:
@circom --version | grep -qF "{{circom_version}}" || \
(echo "circom {{circom_version}} required; got: $(circom --version 2>&1)" >&2; exit 1)
prettify:
nix shell nixpkgs#clang-tools -c clang-format -i src/**.cpp src/**.hpp
@ -10,3 +28,70 @@ sage-run script +args='':
-v "{{justfile_directory()}}:/work" \
-w "/work/$(dirname '{{script}}')" \
sagemath/sagemath sage "$(basename '{{script}}')" {{args}}
# Build the PoQ circuit and its C++ witness generator, equivalent to the CI build.
poq: check-circom
circom blend/poq.circom --c --r1cs --no_asm --O2 --output blend
# 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}/' blend/poq_cpp/main.cpp
cp -r {{src}}/poq blend/poq_cpp/poq
cp {{src}}/circom_adapter.cpp {{src}}/circom_adapter.hpp {{src}}/circom_fwd.hpp {{src}}/types.hpp blend/poq_cpp/
cp {{ci_makefile}} blend/poq_cpp/Makefile
cp blend/test_ffi.cpp blend/poq_cpp/test_ffi.cpp
make -C blend/poq_cpp PROJECT=poq linux-lib
# Run a simple smoke test of the PoQ witness generator.
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: check-circom
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: check-circom
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: check-circom
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 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;
}

7
rust-toolchain.toml Normal file
View File

@ -0,0 +1,7 @@
[toolchain]
# Also, update the version of the nightly toolchain to the latest nightly of the new version specified in the following places:
# * .github/workflows/lint.yml (fmt step)
# * .pre-commit-config.yaml (fmt hook)
channel = "1.95.0"
# Even if clippy should be included in the default profile, in some cases it is not installed. So we force it with an explicit declaration.
components = ["clippy"]

606
rust/Cargo.lock generated Normal file
View File

@ -0,0 +1,606 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bitflags"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
[[package]]
name = "bytes"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
name = "cc"
version = "1.2.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]]
name = "dirs"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.61.2",
]
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
]
[[package]]
name = "fd-lock"
version = "4.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78"
dependencies = [
"cfg-if",
"rustix",
"windows-sys 0.52.0",
]
[[package]]
name = "filetime"
version = "0.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db"
dependencies = [
"cfg-if",
"libc",
"libredox",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "flate2"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "getrandom"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "http"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
dependencies = [
"bytes",
"itoa",
]
[[package]]
name = "httparse"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "libc"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "libredox"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c"
dependencies = [
"bitflags",
"libc",
"plain",
"redox_syscall",
]
[[package]]
name = "linux-raw-sys"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "log"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "logos-blockchain-circuits-build"
version = "0.5.0"
dependencies = [
"dirs",
"fd-lock",
"flate2",
"tar",
"ureq",
]
[[package]]
name = "logos-blockchain-circuits-common"
version = "0.5.0"
dependencies = [
"logos-blockchain-circuits-types",
]
[[package]]
name = "logos-blockchain-circuits-poc-sys"
version = "0.5.0"
dependencies = [
"logos-blockchain-circuits-build",
"logos-blockchain-circuits-common",
"logos-blockchain-circuits-types",
]
[[package]]
name = "logos-blockchain-circuits-pol-sys"
version = "0.5.0"
dependencies = [
"logos-blockchain-circuits-build",
"logos-blockchain-circuits-common",
"logos-blockchain-circuits-types",
]
[[package]]
name = "logos-blockchain-circuits-poq-sys"
version = "0.5.0"
dependencies = [
"logos-blockchain-circuits-build",
"logos-blockchain-circuits-common",
"logos-blockchain-circuits-types",
]
[[package]]
name = "logos-blockchain-circuits-signature-sys"
version = "0.5.0"
dependencies = [
"logos-blockchain-circuits-build",
"logos-blockchain-circuits-common",
"logos-blockchain-circuits-types",
]
[[package]]
name = "logos-blockchain-circuits-types"
version = "0.5.0"
dependencies = [
"bytes",
"libc",
]
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "percent-encoding"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "plain"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_users"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
dependencies = [
"getrandom",
"libredox",
"thiserror",
]
[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom",
"libc",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "rustix"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.61.2",
]
[[package]]
name = "rustls"
version = "0.23.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c2c118cb077cca2822033836dfb1b975355dfb784b5e8da48f7b6c5db74e60e"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pki-types"
version = "1.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9"
dependencies = [
"zeroize",
]
[[package]]
name = "rustls-webpki"
version = "0.103.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "simd-adler32"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tar"
version = "0.4.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973"
dependencies = [
"filetime",
"libc",
"xattr",
]
[[package]]
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0"
dependencies = [
"base64",
"flate2",
"log",
"percent-encoding",
"rustls",
"rustls-pki-types",
"ureq-proto",
"utf8-zero",
"webpki-roots",
]
[[package]]
name = "ureq-proto"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c"
dependencies = [
"base64",
"http",
"httparse",
"log",
]
[[package]]
name = "utf8-zero"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "webpki-roots"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "xattr"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
dependencies = [
"libc",
"rustix",
]
[[package]]
name = "zeroize"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"

192
rust/Cargo.toml Normal file
View File

@ -0,0 +1,192 @@
[workspace.package]
categories = ["cryptography", "external-ffi-bindings"]
description = "Rust bindings for the Logos Blockchain Circuits, providing a safe and efficient interface for interacting with the underlying cryptographic circuits."
edition = "2024"
keywords = ["blockchain", "privacy"]
license = "MIT or Apache-2.0"
readme = "README.md"
repository = "https://github.com/logos-blockchain/logos-blockchain-circuits"
version = "0.5.0"
[workspace]
members = [
"logos-blockchain-circuits-build",
"logos-blockchain-circuits-poc-sys",
"logos-blockchain-circuits-pol-sys",
"logos-blockchain-circuits-poq-sys",
"logos-blockchain-circuits-signature-sys",
"logos-blockchain-circuits-types",
"logos-blockchain-circuits-common",
]
resolver = "3"
[workspace.dependencies]
# Internal
lbc-common = { default-features = false, package = "logos-blockchain-circuits-common", path = "./logos-blockchain-circuits-common" }
lbc-build = { package = "logos-blockchain-circuits-build", path = "./logos-blockchain-circuits-build" }
lbc-poc-sys = { default-features = false, package = "logos-blockchain-circuits-poc-sys", path = "./logos-blockchain-circuits-poc-sys" }
lbc-pol-sys = { default-features = false, package = "logos-blockchain-circuits-pol-sys", path = "./logos-blockchain-circuits-pol-sys" }
lbc-poq-sys = { default-features = false, package = "logos-blockchain-circuits-poq-sys", path = "./logos-blockchain-circuits-poq-sys" }
lbc-signature-sys = { default-features = false, package = "logos-blockchain-circuits-signature-sys", path = "./logos-blockchain-circuits-signature-sys" }
lbc-types = { default-features = false, package = "logos-blockchain-circuits-types", path = "./logos-blockchain-circuits-types" }
# External
dirs = "^6"
fd-lock = "^4"
flate2 = "^1.1"
libc = "^0.2"
tar = "^0.4"
ureq = "^3.3"
bytes = "^1.11"
# Sourced from https://github.com/logos-co/nomos/blob/main/Cargo.toml
[workspace.lints.clippy]
# Cargo and allowed cargo warnings (new lints will warn by default)
cargo = { level = "warn", priority = -1 }
# TODO: We should tackle this lint at some point, to reduce dependencies duplication, reduce compilation times and bring down our binary size.
multiple_crate_versions = { level = "allow" }
# Nursery and allowed nursery warnings (new lints will warn by default)
nursery = { level = "warn", priority = -1 }
# Pedantic and allowed pedantic warnings (new lints will warn by default)
pedantic = { level = "warn", priority = -1 }
similar_names = { level = "allow" }
# Restriction and allowed restriction warnings (new lints will warn by default)
restriction = { level = "warn", priority = -1 }
absolute_paths = { level = "allow" }
alloc_instead_of_core = { level = "allow" }
arbitrary_source_item_ordering = { level = "allow" }
big_endian_bytes = { level = "allow" }
blanket_clippy_restriction_lints = { level = "allow" }
decimal_literal_representation = { level = "allow" }
default_numeric_fallback = { level = "allow" }
deref_by_slicing = { level = "allow" }
else_if_without_else = { level = "allow" }
exhaustive_enums = { level = "allow" }
exhaustive_structs = { level = "allow" }
exit = { level = "allow" }
expect_used = { level = "allow" }
field_scoped_visibility_modifiers = { level = "allow" }
float_arithmetic = { level = "allow" }
get_unwrap = { level = "allow" }
host_endian_bytes = { level = "allow" }
implicit_return = { level = "allow" }
integer_division_remainder_used = { level = "allow" }
iter_over_hash_type = { level = "allow" }
let_underscore_must_use = { level = "allow" }
let_underscore_untyped = { level = "allow" }
little_endian_bytes = { level = "allow" }
map_err_ignore = { level = "allow" }
min_ident_chars = { level = "allow" }
missing_asserts_for_indexing = { level = "allow" }
missing_docs_in_private_items = { level = "allow" }
missing_inline_in_public_items = { level = "allow" }
missing_trait_methods = { level = "allow" }
mixed_read_write_in_expression = { level = "allow" }
mod_module_files = { level = "allow" }
module_name_repetitions = { level = "allow" }
modulo_arithmetic = { level = "allow" }
panic = { level = "allow" }
panic_in_result_fn = { level = "allow" }
partial_pub_fields = { level = "allow" }
print_stderr = { level = "allow" }
print_stdout = { level = "allow" }
pub_use = { level = "allow" }
pub_with_shorthand = { level = "allow" }
question_mark_used = { level = "allow" }
self_named_module_files = { level = "allow" }
semicolon_inside_block = { level = "allow" }
single_call_fn = { level = "allow" }
single_char_lifetime_names = { level = "allow" }
std_instead_of_alloc = { level = "allow" }
std_instead_of_core = { level = "allow" }
struct_field_names = { level = "allow" }
unseparated_literal_suffix = { level = "allow" }
use_debug = { level = "allow" }
wildcard_enum_match_arm = { level = "allow" }
# TODO: Address these lints at some point. To allow them, move them to the section where they belong (e.g., pedantic), and keep allowing them. To enforce them, since all lints are "warn" by default, just remove them from this list.
arithmetic_side_effects = { level = "allow" }
as_conversions = { level = "allow" }
as_pointer_underscore = { level = "allow" }
as_underscore = { level = "allow" }
assertions_on_result_states = { level = "allow" }
cast_possible_truncation = { level = "allow" }
cast_possible_wrap = { level = "allow" }
cast_precision_loss = { level = "allow" }
cast_sign_loss = { level = "allow" }
doc_paragraphs_missing_punctuation = { level = "allow" }
error_impl_error = { level = "allow" }
impl_trait_in_params = { level = "allow" }
indexing_slicing = { level = "allow" }
infinite_loop = { level = "allow" }
integer_division = { level = "allow" }
large_stack_frames = { level = "allow" }
missing_assert_message = { level = "allow" }
missing_errors_doc = { level = "allow" }
missing_panics_doc = { level = "allow" }
pattern_type_mismatch = { level = "allow" }
redundant_test_prefix = { level = "allow" }
ref_patterns = { level = "allow" }
renamed_function_params = { level = "allow" }
same_name_method = { level = "allow" }
shadow_reuse = { level = "allow" }
shadow_same = { level = "allow" }
shadow_unrelated = { level = "allow" }
tests_outside_test_module = { level = "allow" }
todo = { level = "allow" }
unchecked_time_subtraction = { level = "allow" }
unimplemented = { level = "allow" }
unreachable = { level = "allow" }
unwrap_in_result = { level = "allow" }
unwrap_used = { level = "allow" }
[workspace.lints.rust]
# Explicitly allowed lints
unused_crate_dependencies = { level = "allow" } # Too many false positives especially around benchmarking and binaries, which do not have their own `dependencies` section yet. Plus, we have cargo-machete checking unused deps.
unused_results = { level = "allow" } # We have Clippy lints to warn on unused `must_use` results. This is too pedantic as it complains on EVERY unused result.
# Lints which are allow-by-default but have been changed to "warn"
ambiguous_negative_literals = { level = "warn" }
closure_returning_async_block = { level = "warn" }
deref_into_dyn_supertrait = { level = "warn" }
impl_trait_redundant_captures = { level = "warn" }
let_underscore_drop = { level = "warn" }
macro_use_extern_crate = { level = "warn" }
missing_unsafe_on_extern = { level = "warn" }
redundant_imports = { level = "warn" }
redundant_lifetimes = { level = "warn" }
single_use_lifetimes = { level = "warn" }
tail_expr_drop_order = { level = "warn" }
trivial_numeric_casts = { level = "warn" }
unit_bindings = { level = "warn" }
unsafe_attr_outside_unsafe = { level = "warn" }
unsafe_op_in_unsafe_fn = { level = "warn" }
unstable_features = { level = "warn" }
unused_extern_crates = { level = "warn" }
unused_import_braces = { level = "warn" }
unused_lifetimes = { level = "warn" }
unused_macro_rules = { level = "warn" }
unused_qualifications = { level = "warn" }
# TODO: Address these allow-by-default Rustc lints at some point. To allow them, move them to the list of explicitly allowed lints. To enforce them, move them to the list of explicitly warned ones.
absolute_paths_not_starting_with_crate = { level = "allow" }
elided_lifetimes_in_paths = { level = "allow" }
ffi_unwind_calls = { level = "allow" }
impl_trait_overcaptures = { level = "allow" }
linker_messages = { level = "allow" }
missing_copy_implementations = { level = "allow" }
missing_debug_implementations = { level = "allow" }
missing_docs = { level = "allow" }
trivial_casts = { level = "allow" }
unreachable_pub = { level = "allow" }
unsafe_code = { level = "allow" }
variant_size_differences = { level = "allow" }

2
rust/README.md Normal file
View File

@ -0,0 +1,2 @@
# Logos Blockchain Circuits Rust
This directory contains the Rust FFI to interact with the Logos Blockchain Circuits.

View File

@ -0,0 +1,23 @@
[package]
name = "logos-blockchain-circuits-build"
categories.workspace = true
description = "Shared build script logic for Logos Blockchain Circuits sys crates."
edition.workspace = true
keywords.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
version.workspace = true
[features]
prebuilt = ["dep:dirs", "dep:fd-lock", "dep:flate2", "dep:tar", "dep:ureq"]
[lints]
workspace = true
[dependencies]
dirs = { workspace = true, optional = true }
fd-lock = { workspace = true, optional = true }
flate2 = { workspace = true, optional = true }
tar = { workspace = true, optional = true }
ureq = { workspace = true, optional = true }

View File

@ -0,0 +1,161 @@
use std::path::PathBuf;
#[cfg(feature = "prebuilt")]
mod prebuilt {
use std::path::{Path, PathBuf};
use ureq::Body;
use ureq::http::Response;
static REPO: &str = "logos-blockchain/logos-blockchain-circuits";
static ARTIFACT_PREFIX: &str = "logos-blockchain-circuits";
fn build_artifact_name(version: &str, os: &str, arch: &str) -> String {
format!("{ARTIFACT_PREFIX}-v{version}-{os}-{arch}")
}
fn build_artifact_url(version: &str, os: &str, arch: &str) -> String {
let artifact = build_artifact_name(version, os, arch);
let artifact_tar_gz = format!("{artifact}.tar.gz");
format!("https://github.com/{REPO}/releases/download/v{version}/{artifact_tar_gz}")
}
fn fetch_library(version: &str, os: &str, arch: &str, lib_var_name: &str) -> Response<Body> {
let url = build_artifact_url(version, os, arch);
// We skip checksum verification intentionally.
// Hardcoded hashes would protect against a silently replaced release asset but require a
// two-step release (build → hash → commit → tag) which feels overkill for a first-party
// library.
ureq::get(&url).call().unwrap_or_else(|error| {
panic!(
"Failed to download a prebuilt library for {os}-{arch} v{version}: {error}. \
Set {lib_var_name} to point to a local build instead."
)
})
}
fn unpack_library(
response: Response<Body>,
circuit_name: &str,
version: &str,
os: &str,
arch: &str,
output_dir: &Path,
) -> PathBuf {
let gz_decoder = flate2::read::GzDecoder::new(response.into_body().into_reader());
let mut archive = tar::Archive::new(gz_decoder);
archive
.unpack(output_dir)
.expect("Failed to unpack the downloaded archive.");
let unpacked_artifact_path = output_dir.join(build_artifact_name(version, os, arch));
let unpacked_library_directory = unpacked_artifact_path.join(circuit_name);
assert!(
unpacked_library_directory.is_dir(),
"Failed to find the unpacked library at {}",
unpacked_library_directory.display()
);
unpacked_library_directory
}
/// Produce a lockfile for the given directory
fn get_lockfile(directory: &Path) -> fd_lock::RwLock<std::fs::File> {
let file = std::fs::OpenOptions::new()
.create(true)
.truncate(false)
.write(true)
.open(directory.join(".lock"))
.expect("Failed to open cache lock file.");
fd_lock::RwLock::new(file)
}
fn get_cache_dir() -> PathBuf {
dirs::cache_dir()
.expect("Could not determine the cache directory for this platform.")
.join("logos")
.join("blockchain")
}
pub fn provision_library(circuit_name: &str, lib_var_name: &str) -> PathBuf {
let version = env!("CARGO_PKG_VERSION");
let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
let cache = get_cache_dir();
// The tarball unpacks to a top-level `{artifact_name}/` dir, so the circuit lives at
// `cache/{artifact_name}/{circuit_name}/`.
let circuit_dir = cache
.join(build_artifact_name(version, &os, &arch))
.join(circuit_name);
std::fs::create_dir_all(&cache).expect("Failed to create the cache directory.");
// Since the circuits' libraries are all contained in the same single artifact, each crate
// will try to download the same circuits.
// To avoid redundant downloads, we use a lock to ensure that only one process fetches the
// circuits while the others wait for it to complete and then re-check the cache.
let mut lock = get_lockfile(&cache);
let _guard = lock.write().expect("Failed to acquire cache lock.");
if circuit_dir.is_dir() {
println!(
"Found a cached {circuit_name} library at {}, reusing.",
circuit_dir.display()
);
return circuit_dir;
}
println!(
"No cached download found, downloading {circuit_name} v{version} for {os}-{arch}..."
);
let response = fetch_library(version, &os, &arch, lib_var_name);
println!("Download complete, unpacking...");
let lib_dir = unpack_library(response, circuit_name, version, &os, &arch, &cache);
println!("Ready, {circuit_name} library at {}.", lib_dir.display());
lib_dir
}
}
pub fn build(circuit_name: &str, lib_var_name: &str) {
println!("cargo:rerun-if-env-changed={lib_var_name}");
println!("cargo:rerun-if-env-changed=CARGO_PKG_VERSION");
println!("cargo:rerun-if-changed=Cargo.toml");
println!("cargo:rerun-if-changed=build.rs");
let lib_dir = std::env::var(lib_var_name).map_or_else(
|_| {
#[cfg(not(feature = "prebuilt"))]
panic!(
"{lib_var_name} is not set. Either:\n\
- Set {lib_var_name} to point at a local build, or\n\
- Enable the `prebuilt` feature to download from GitHub Releases."
);
#[cfg(feature = "prebuilt")]
{
println!("{lib_var_name} not set, falling back to prebuilt download.");
prebuilt::provision_library(circuit_name, lib_var_name)
}
},
|lib_dir| {
println!("Found {lib_var_name}, using local library at {lib_dir}.");
let lib_dir_path = PathBuf::from(lib_dir);
assert!(
lib_dir_path.is_dir(),
"The library directory specified in {lib_var_name} at {} does not exist.",
lib_dir_path.display()
);
lib_dir_path
},
);
let lib_dir = lib_dir
.to_str()
.expect("Failed to convert the library directory path to a string");
println!("cargo:rustc-env={lib_var_name}={lib_dir}");
println!("cargo:rustc-link-search=native={lib_dir}");
println!("cargo:rustc-link-lib=static={circuit_name}");
println!("cargo:rustc-link-lib=stdc++");
println!("cargo:rustc-link-lib=gmp");
}

View File

@ -0,0 +1,16 @@
[package]
name = "logos-blockchain-circuits-common"
categories.workspace = true
description.workspace = true
edition.workspace = true
keywords.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
version.workspace = true
[lints]
workspace = true
[dependencies]
lbc-types = { workspace = true }

View File

@ -0,0 +1 @@
pub mod string;

View File

@ -0,0 +1,20 @@
use lbc_types::native::Error;
use std::path::Path;
pub fn as_null_terminated_string(string: &str) -> Result<std::ffi::CString, Error> {
std::ffi::CString::new(string).map_err(|error| {
Error::InvalidInput(Some(format!(
"Could not convert string to CString: {error}"
)))
})
}
pub fn path_as_null_terminated_string(path: &Path) -> Result<std::ffi::CString, Error> {
let path = path.to_str().ok_or_else(|| {
Error::InvalidInput(Some(format!(
"Could not convert the path to a string: {}",
path.display()
)))
})?;
as_null_terminated_string(path)
}

View File

@ -0,0 +1,23 @@
[package]
name = "logos-blockchain-circuits-poc-sys"
categories.workspace = true
description.workspace = true
edition.workspace = true
keywords.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
version.workspace = true
[features]
prebuilt = ["lbc-build/prebuilt"]
[lints]
workspace = true
[dependencies]
lbc-types = { workspace = true }
lbc-common = { workspace = true }
[build-dependencies]
lbc-build = { workspace = true }

View File

@ -0,0 +1,3 @@
fn main() {
lbc_build::build("poc", "LBC_POC_LIB_DIR");
}

View File

@ -0,0 +1,73 @@
{
"secret_voucher": "967693750741252580129370741601438872183843430156721776754788549286651677750",
"voucher_merkle_path": [
"6760957519373942083753863818365891569838081322706287060861024958049616883872",
"4465044690877673959379630031085790488013857563988547679987435075304500933954",
"13324732284754601104005935378955314380888360214440369094293125852402660947587",
"751846428059524824613704285163911088459476541213765124209950324431915959831",
"6038709039063684132786699728641459669752757359588522148066703796251420937549",
"13738028016996204322086995856054471548830485529018768524176769778055121664207",
"3247811310761913519394659530300211882093244684976422241891466731923117641924",
"17759434733520999094127756196564286982134377874670985981967107166244476114191",
"10439394532189975251676889261553856687018976136540996547523529229452985841894",
"21817985363519865032455212452700263411567232861782241553352701809413458230205",
"12743546576539988493605660695948979780445141638209080027367248079788465306875",
"10486283397124108182476342309962010457564415255711484724372810865359447030074",
"7732396266725234558621644026839536297549144669187219414595824396993766503667",
"9218727972992690804265624045300987755393869081123043258395582561748980225779",
"12293828374871695333583884879696806078489804229410850142490615473351272368055",
"15676114048570936960535525197542551243483740313061450270646442649470011330319",
"7788186880839575813391082864142092937468972200299626575231867739594374584354",
"16312033345360983679946394715036243256734413200471618477700490413711855126671",
"16962284811957205674976016068974035759915296114616020584303314736345193043585",
"7561654244528652857908285352009139941416843534230515440422770887337004738701",
"1221817097944787806648540754395537787299626178709583354368288286078980967641",
"13734831143243391544582579659353150156408343223164222563626930619372712282469",
"8844967953129979365866630761540941039109023500226162691150639579280951984915",
"2720862664779223685514929282617191922009465878195616364110482909635551933798",
"4164638788145133427412986511346875737261315680675887710671119072340972066260",
"9477784803841584355604716743687988183501535661924299108331302040811843685494",
"19779338607502121804566093813321090012599307850902677285205344497085666699877",
"14629589865216166173500986311834933690566596772333573563533435637260058354590",
"16491383351556021779681173490637304596585835849620638208559205306534152597710",
"2567887307252850771864398053039231796936889280823431337567399212882570032944",
"17128149651933896729510402797195069638613831606426831356737111104282458862374",
"21548396224793452403247607341264541760071256504082507925892356245056739977676"
],
"voucher_merkle_path_selectors": [
"0",
"1",
"1",
"1",
"1",
"0",
"0",
"1",
"1",
"0",
"0",
"1",
"0",
"1",
"0",
"0",
"0",
"0",
"1",
"1",
"1",
"1",
"1",
"0",
"1",
"0",
"1",
"1",
"1",
"0",
"1",
"1"
],
"mantle_tx_hash": "13403607866351080013115672688878717407774000351312443676099225335214329491209",
"voucher_root": "20810875415353676096192834577269613981524168537821543897016159330974871397924"
}

View File

@ -0,0 +1,12 @@
use lbc_types::ffi::{Bytes, Status, WitnessInput};
use std::ffi::c_char;
unsafe extern "C" {
pub fn poc_generate_witness(input: *const WitnessInput, output: *mut Bytes) -> Status;
pub fn poc_generate_witness_from_files(
dat: *const c_char,
inputs: *const c_char,
output: *const c_char,
) -> Status;
}

View File

@ -0,0 +1,4 @@
mod ffi;
pub mod native;
pub use native::{PocWitnessInput, generate_witness, generate_witness_from_files};

View File

@ -0,0 +1,82 @@
use crate::ffi::{poc_generate_witness, poc_generate_witness_from_files};
use lbc_common::string::path_as_null_terminated_string;
use lbc_types::{
ffi,
native::{Error, Witness},
};
use std::path::Path;
static RAW_CIRCUIT_DAT: &[u8] =
include_bytes!(concat!(env!("LBC_POC_LIB_DIR"), "/witness_generator.dat"));
pub struct PocDat;
impl<'dat> lbc_types::CircuitDat<'dat> for PocDat {
const DAT: &'dat [u8] = RAW_CIRCUIT_DAT;
}
pub type PocWitnessInput<'dat> = lbc_types::CircuitWitnessInput<'dat, PocDat>;
pub fn generate_witness(input: &PocWitnessInput) -> Result<Witness, Error> {
let ffi_input_guard = input.as_ffi();
let ffi_input = ffi_input_guard.as_ref();
let mut ffi_output_bytes = ffi::Bytes::null();
// SAFETY: ffi_input is a valid pointer and ffi_output_bytes is a locally initialized null Bytes.
let status =
unsafe { poc_generate_witness(std::ptr::from_ref(ffi_input), &raw mut ffi_output_bytes) };
status.try_into().map(|()| Witness::from(ffi_output_bytes))
}
pub fn generate_witness_from_files(dat: &Path, inputs: &Path, output: &Path) -> Result<(), Error> {
let c_dat = path_as_null_terminated_string(dat)?;
let c_inputs = path_as_null_terminated_string(inputs)?;
let c_output = path_as_null_terminated_string(output)?;
// SAFETY: c_dat, c_inputs, and c_output are valid null-terminated C strings for the duration of the call.
unsafe { poc_generate_witness_from_files(c_dat.as_ptr(), c_inputs.as_ptr(), c_output.as_ptr()) }
.try_into()
}
#[cfg(test)]
mod tests {
use super::{PocWitnessInput, generate_witness, generate_witness_from_files};
use std::path::PathBuf;
use std::sync::LazyLock;
static LIB_DIR: LazyLock<PathBuf> = LazyLock::new(|| {
const ENV_VAR: &str = "LBC_POC_LIB_DIR";
PathBuf::from(
std::env::var(ENV_VAR).unwrap_or_else(
|_| panic!("Environment variable '{ENV_VAR}' must be available, as provided by the build script."),
)
)
});
static INPUTS: LazyLock<PathBuf> =
LazyLock::new(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json"));
#[test]
fn test_generate_witness() {
let dat = LIB_DIR.join("witness_generator");
let witness_output_path = std::env::temp_dir().join("poc_test_witness.wtns");
generate_witness_from_files(&dat, &INPUTS, &witness_output_path)
.expect("generate_witness_from_files failed.");
let inputs_json = std::fs::read_to_string(&*INPUTS)
.unwrap_or_else(|_| panic!("Failed to read {}.", INPUTS.display()));
let input = PocWitnessInput::new(inputs_json)
.expect("Failed to construct the input for the witness generator.");
let output = generate_witness(&input).expect("generate_witness failed.");
let expected = std::fs::read(&witness_output_path).unwrap_or_else(|_| {
panic!(
"Failed to read the generated witness from {}.",
witness_output_path.display()
)
});
assert_eq!(output.as_ref().iter().as_slice(), expected.as_slice());
}
}

View File

@ -0,0 +1,23 @@
[package]
name = "logos-blockchain-circuits-pol-sys"
categories.workspace = true
description.workspace = true
edition.workspace = true
keywords.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
version.workspace = true
[features]
prebuilt = ["lbc-build/prebuilt"]
[lints]
workspace = true
[dependencies]
lbc-types = { workspace = true }
lbc-common = { workspace = true }
[build-dependencies]
lbc-build = { workspace = true }

View File

@ -0,0 +1,3 @@
fn main() {
lbc_build::build("pol", "LBC_POL_LIB_DIR");
}

View File

@ -0,0 +1,150 @@
{
"sl": "2",
"epoch_nonce": "1",
"t0": "2473484656031743130624528499274307172369787260057014856309956995191444674",
"t1": "21888242732080995413138029684141064335057134963969188692069153164444508170606",
"secret_key": "7329962581074531645140456897688060564526637306306408058828300122976166982059",
"P_lead_part_one": "123456",
"P_lead_part_two": "654321",
"noteid_aged_path": [
"15902188246945297660745639816877940054796159025125590367999255023125739397652",
"17428337904521747122246156920806505766773506916800844590758448420881698740105",
"468183280517368641419356975404050946184194985879608625527256681081514196098",
"9857356446423834251015544624540902002861921286200857918125548693293771474746",
"12530440301501032173254115146178387369899096696476921293807852624223441454699",
"9102552235494849568056879081176958190502758901529745872149615078913093110367",
"15332836703551879045647234301826436702072998048558632819831343618269234606372",
"15952532935258781628726365207412031354751070204874462086915985737634285658345",
"11617186336877516205132474707629582846966589234021946714155218394831358857247",
"17080789484016787310502590647309822431255976143447625559555674333524647440067",
"18659199795786635596293350923127416814662448928926239356418915092401949789788",
"1043038124308436734336560531169206604082177989318077248264344526382690786982",
"7003765854548265669971916203377800937290020471642328566366870448531962095529",
"11050236989616267237111870236974875566108700565568898771969074233035618350002",
"2533751737509759123516379686699492918377283972510738532508379695975052244036",
"10434256768101250515198083337558660215927337348287005027389881302147240005154",
"12837725658175272451154613854148629711893386915133752900810503335519991343655",
"12700541049942924863612541852506887422731234828020365455762737916860175789704",
"12573741914276908448045980903279034749900953112549139654805121785204580357207",
"8968254178272762157895707330407008387111599719645052181828541862302266239216",
"20742874131443474687782470845270548443698769223982267584726522628668308608112",
"9999767371011565944639501141012524874493683597568729920926334390830396753704",
"12374534369774902727257888918508979001911705274820078145646739679533754573424",
"19268235150374693171101451101258052228702701088784125163210986110908874575894",
"441332906538295507239051937515741879494109540079798934075454530577366571492",
"6129618356747130372168909649376280133745354921100135443992798222259603841569",
"4496649267081857811919738457357059245915720956181270410313546537907780142035",
"12743126317937809671495985340192176235891732494688481786128621476821474665303",
"901840713684703225385820874490383670490483483080770337662579043488775296023",
"10927577986293549543779667277886325733953085688298112393252336467344863880365",
"8473888800279268472161506886879723967558714367499595291224158750669832531158",
"19721447853263860379579181356058567689083097845640785640187003258546563439277"
],
"noteid_aged_selectors": [
"1",
"1",
"1",
"0",
"1",
"0",
"1",
"1",
"0",
"1",
"0",
"1",
"0",
"0",
"0",
"0",
"1",
"1",
"0",
"1",
"0",
"1",
"1",
"0",
"0",
"0",
"0",
"1",
"1",
"1",
"1",
"1"
],
"ledger_aged": "9907496234164738674719754286318998202143315407023653151112941050435603056651",
"note_tx_hash": "20223528014093812977541895123120747633533656837394263567369465802146525334433",
"note_output_number": "422",
"noteid_latest_path": [
"20483380182942370599276460472822047910695963897520228620026114079480834642554",
"8651317076561654292026377585519179675049184842398465650659084128411469650526",
"21425771387649153085202676968883939741776698606005720713797165628391391510570",
"2464861242004047676413225182531440736105723323417843370490252383311677571678",
"19491453917823191068924688076595231671940471274477527498481756007196499447649",
"1176748827065542272964701141107094149522420759289388172293118512335294635041",
"3299427024440722918686537545360805925565544865914895921705713657483198282370",
"8676294931486161402217736786136350680814737832309086766645947290346485301305",
"1194490288177404655558854450331655377836115194134883251216647918413934872450",
"15553436680567018414748245524365698672469393742981821478321757836296924702208",
"18760606749355768642525698838451959355190742805724305319741610837135438843793",
"6161496781920499484106873453663059897931985922037996995917393202282946916344",
"17705630684705199458140820935591248964213782627117437314477744571443032304952",
"9724274259568537436782348598156920528128138808555843188427494557472321468949",
"5466817634746337628333298578810978989067001440642192446558436227084941093222",
"16494041847547992223313095508652287702639424702153814725128657709203320143841",
"6528170446718511127086652275105019639788731466250056396450984516917214341997",
"6561500772488498465085378724738264583497130751949261757862310345373405719352",
"3815120747999928230097864671072510355895072895139778010596771856349290188351",
"17303101039105503728506358403584291694929820278543186462559922466312610206586",
"6226574607729834489174079778279178763707817357653295885077105357079469092244",
"21729366069495375565183890745946535138952895325166816652864384287210295488490",
"14537848505494375072543340632196042321038883976220184774436389059255730709737",
"9163027709361837575547371418304288816555790532858870081510396295711756310482",
"6156464793863118277056215011382689237775042877662370243112733741034240082502",
"20844456742401181559479512632286351148046075535736938039088626964972929711601",
"2347402598418747187534401798976124993245365457072161587157981228855029862628",
"20014696468373915559760277064758449323508513383406906656096464817687259375428",
"8220804518985235671089333458601569928688446715891843624949744388799485342584",
"20585877011082131167343660455058571070757301112744161576050391774335375778890",
"16378233495401884641259747182647176572407610489279387832117800731619897286201",
"14220045659305316526516794332224144375368271853520372229536276752477675518408"
],
"noteid_latest_selectors": [
"0",
"1",
"0",
"1",
"0",
"0",
"1",
"1",
"1",
"0",
"0",
"0",
"0",
"0",
"1",
"1",
"0",
"0",
"1",
"0",
"0",
"1",
"0",
"1",
"1",
"0",
"0",
"0",
"1",
"1",
"1",
"1"
],
"ledger_latest": "19194142281236793845067321500021419635169885917260820016897850403558817637963",
"v": "3"
}

View File

@ -0,0 +1,12 @@
use lbc_types::ffi::{Bytes, Status, WitnessInput};
use std::ffi::c_char;
unsafe extern "C" {
pub fn pol_generate_witness(input: *const WitnessInput, output: *mut Bytes) -> Status;
pub fn pol_generate_witness_from_files(
dat: *const c_char,
inputs: *const c_char,
output: *const c_char,
) -> Status;
}

View File

@ -0,0 +1,4 @@
mod ffi;
pub mod native;
pub use native::{PolWitnessInput, generate_witness, generate_witness_from_files};

View File

@ -0,0 +1,82 @@
use crate::ffi::{pol_generate_witness, pol_generate_witness_from_files};
use lbc_common::string::path_as_null_terminated_string;
use lbc_types::{
ffi,
native::{Error, Witness},
};
use std::path::Path;
static RAW_CIRCUIT_DAT: &[u8] =
include_bytes!(concat!(env!("LBC_POL_LIB_DIR"), "/witness_generator.dat"));
pub struct PolDat;
impl<'dat> lbc_types::CircuitDat<'dat> for PolDat {
const DAT: &'dat [u8] = RAW_CIRCUIT_DAT;
}
pub type PolWitnessInput<'dat> = lbc_types::CircuitWitnessInput<'dat, PolDat>;
pub fn generate_witness(input: &PolWitnessInput) -> Result<Witness, Error> {
let ffi_input_guard = input.as_ffi();
let ffi_input = ffi_input_guard.as_ref();
let mut ffi_output_bytes = ffi::Bytes::null();
// SAFETY: ffi_input is a valid pointer and ffi_output_bytes is a locally initialized null Bytes.
let status =
unsafe { pol_generate_witness(std::ptr::from_ref(ffi_input), &raw mut ffi_output_bytes) };
status.try_into().map(|()| Witness::from(ffi_output_bytes))
}
pub fn generate_witness_from_files(dat: &Path, inputs: &Path, output: &Path) -> Result<(), Error> {
let c_dat = path_as_null_terminated_string(dat)?;
let c_inputs = path_as_null_terminated_string(inputs)?;
let c_output = path_as_null_terminated_string(output)?;
// SAFETY: c_dat, c_inputs, and c_output are valid null-terminated C strings for the duration of the call.
unsafe { pol_generate_witness_from_files(c_dat.as_ptr(), c_inputs.as_ptr(), c_output.as_ptr()) }
.try_into()
}
#[cfg(test)]
mod tests {
use super::{PolWitnessInput, generate_witness, generate_witness_from_files};
use std::path::PathBuf;
use std::sync::LazyLock;
static LIB_DIR: LazyLock<PathBuf> = LazyLock::new(|| {
const ENV_VAR: &str = "LBC_POL_LIB_DIR";
PathBuf::from(
std::env::var(ENV_VAR).unwrap_or_else(
|_| panic!("Environment variable '{ENV_VAR}' must be available, as provided by the build script."),
)
)
});
static INPUTS: LazyLock<PathBuf> =
LazyLock::new(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json"));
#[test]
fn test_generate_witness() {
let dat = LIB_DIR.join("witness_generator");
let witness_output_path = std::env::temp_dir().join("pol_test_witness.wtns");
generate_witness_from_files(&dat, &INPUTS, &witness_output_path)
.expect("generate_witness_from_files failed.");
let inputs_json = std::fs::read_to_string(&*INPUTS)
.unwrap_or_else(|_| panic!("Failed to read {}.", INPUTS.display()));
let input = PolWitnessInput::new(inputs_json)
.expect("Failed to construct the input for the witness generator.");
let output = generate_witness(&input).expect("generate_witness failed.");
let expected = std::fs::read(&witness_output_path).unwrap_or_else(|_| {
panic!(
"Failed to read the generated witness from {}.",
witness_output_path.display()
)
});
assert_eq!(output.as_ref().iter().as_slice(), expected.as_slice());
}
}

View File

@ -0,0 +1,23 @@
[package]
name = "logos-blockchain-circuits-poq-sys"
categories.workspace = true
description.workspace = true
edition.workspace = true
keywords.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
version.workspace = true
[features]
prebuilt = ["lbc-build/prebuilt"]
[lints]
workspace = true
[dependencies]
lbc-types = { workspace = true }
lbc-common = { workspace = true }
[build-dependencies]
lbc-build = { workspace = true }

View File

@ -0,0 +1,3 @@
fn main() {
lbc_build::build("poq", "LBC_POQ_LIB_DIR");
}

View File

@ -0,0 +1,132 @@
{
"session": "1",
"core_quota": "10",
"leader_quota": "5",
"core_root": "20423847801203321296654759690878714805328777188198550442842378955293864405749",
"pol_ledger_aged": "17903920406556048980238705683215862296511171897974321387437239009873416173088",
"K_part_one": "123456",
"K_part_two": "654321",
"selector": "0",
"index": "4",
"core_sk": "9648814259696994178754585280937762562521917479862309330976828088416086689502",
"core_path": [
"7444981793259323545028230817540266139574655346498587207322690091155864366514",
"11906618682557735136285409970678605865823718647884839058358264586062323572931",
"20134132405688011859463119692951856666179032704702165461712292610201307937895",
"14957940720199233077855574470291237736607251243547208306826060526364053594567",
"9960019677323356900045391326995069522689045788302673979918902013710931552405",
"66857460143066791244205132003829555578944395751024287435105865077587183768",
"9684190057075151027164519297536804034845411696380043986044825589786854843890",
"5454398284364625202375600088042852298143548338598297694708920725120268102172",
"17717838934596276392301608861658238780702831838416312046180761451761658981677",
"21049060929819155971068746329904761210986436808741330971979776532239897765447",
"11774149665086408677974469133186402228920447314607286251417731337331983592621",
"20730401134454070553472357967443459347591806294042271432905369723302561196146",
"8683835652829558567596368905844156962511421729430671198119438457363223237488",
"15354188356303619925782257044335868802108846851399355235348014093341533049042",
"4726038353749363201132655543916148764086757634128462972437143285566298790278",
"19793785327783426164764238122458972627129812758755648364664517341770465698137",
"12144554112696250636735897359535254161697584560941692171669941649860599557215",
"4391930156296016129347969147731784916200749354398548728573386759785083449305",
"9056244932796998900758180921717949733339642790698415152308909239074793689860",
"20440861423381563032522290464977155181939164858308810772959774388792060977834"
],
"core_path_selectors": [
"0",
"1",
"1",
"0",
"1",
"0",
"1",
"1",
"1",
"0",
"1",
"1",
"0",
"1",
"1",
"0",
"0",
"1",
"0",
"1"
],
"pol_sl": "2999142337",
"pol_epoch_nonce": "13416107872788342879859909752417297479970876884105393946206193406636245124924",
"pol_t0": "148409079361904587837471709956458430342187235603420891378597419711486680",
"pol_t1": "21888242871336145414933615591437256729835795974444825699352339602896135814447",
"pol_secret_key": "5888730225944530149138538772959785628943021630629194621324636699030154327779",
"pol_noteid_path": [
"11417212955939342484156122492131731680470736064572423108264714804692982132423",
"13195529687976077417120457189120231305870945642160590995096417385531885506527",
"8472612035444494025196025434618086294389115909303446215474787216226075733233",
"4395544485586958768290328184233580023235668872384703046186144832197262227824",
"4155097203117131499585729150758272768367941952959383568978188417941807061285",
"12408098106443635130911956104856030560309651099061156514970962447957027050139",
"18743723426188623742373733557975920292757537418376825891131826727792647674843",
"8921759404097162857845142647448014820935511584403450140750072994549272153730",
"12780803834914504736250867272694468211504307499239030290058679926157641539231",
"14426829036224566782069722269326129726234814130810124541609677896636347739469",
"12104693267449791844517047312768375964966170218283608605832423892888932116836",
"4883118865130876989815011894870188012768758733041802381045504888060618022896",
"221076344526264664133449802750892395792062472746642224588395874880886057862",
"5113214819332200669368049090534645222011919607097424838023324847441692662817",
"11190105598795402691316927391391967636268949446132854344954730333558274776561",
"9497071318258212130830107535590335197992746719505860281792486969560303727415",
"748420254289718676536943619836584348410332952487466816579920043963946022247",
"14115148735545746869495305176487552481901196697644225252711231121645192222529",
"9825912699605171279245430402178700385570068146465368260071935364769450924475",
"1501769250930508362584740947030050914124963948804793138968307230445384260381",
"17449384761389191557925767178974603169885098072345898391638302374310429932515",
"15254499277889636076581272515864557232200527228506352325296439482258130354292",
"17609649632230164263025759476016886422378093160688512447628349616375377913864",
"3796675673265260264289049510567646443858253346231355244420810954931342727796",
"16421896950452608279534605320939669191650110271813875344999112106538557831552",
"17833913909116979902116409442832814514923484644816639363063290974377083565699",
"6674420083709581276404439792277275401604929658229769714363091874557852768901",
"413703733576599351434912508015408034175577472292919474661639467491474199758",
"8269883007022448705723402393227587978527160373548295017073655056451421211755",
"2524601535002894286006232081527402693925618141420537796447547436812892177839",
"18128917825606990579294624696080518601859437342124614427642120271129736700878",
"20950332433104838321486108855942309235493780674594737746438717221993421294744"
],
"pol_noteid_path_selectors": [
"1",
"1",
"1",
"0",
"0",
"0",
"1",
"1",
"1",
"1",
"0",
"1",
"0",
"0",
"1",
"0",
"1",
"1",
"1",
"1",
"0",
"1",
"0",
"1",
"1",
"0",
"0",
"1",
"1",
"1",
"0",
"0"
],
"pol_note_tx_hash": "10190023498178194208361021687460995025878664681917212352967702284591462728108",
"pol_note_output_number": "449",
"pol_note_value": "50"
}

View File

@ -0,0 +1,12 @@
use lbc_types::ffi::{Bytes, Status, WitnessInput};
use std::ffi::c_char;
unsafe extern "C" {
pub fn poq_generate_witness(input: *const WitnessInput, output: *mut Bytes) -> Status;
pub fn poq_generate_witness_from_files(
dat: *const c_char,
inputs: *const c_char,
output: *const c_char,
) -> Status;
}

View File

@ -0,0 +1,4 @@
mod ffi;
pub mod native;
pub use native::{PoqWitnessInput, generate_witness, generate_witness_from_files};

View File

@ -0,0 +1,82 @@
use crate::ffi::{poq_generate_witness, poq_generate_witness_from_files};
use lbc_common::string::path_as_null_terminated_string;
use lbc_types::{
ffi,
native::{Error, Witness},
};
use std::path::Path;
static RAW_CIRCUIT_DAT: &[u8] =
include_bytes!(concat!(env!("LBC_POQ_LIB_DIR"), "/witness_generator.dat"));
pub struct PoqDat;
impl<'dat> lbc_types::CircuitDat<'dat> for PoqDat {
const DAT: &'dat [u8] = RAW_CIRCUIT_DAT;
}
pub type PoqWitnessInput<'dat> = lbc_types::CircuitWitnessInput<'dat, PoqDat>;
pub fn generate_witness(input: &PoqWitnessInput) -> Result<Witness, Error> {
let ffi_input_guard = input.as_ffi();
let ffi_input = ffi_input_guard.as_ref();
let mut ffi_output_bytes = ffi::Bytes::null();
// SAFETY: ffi_input is a valid pointer and ffi_output_bytes is a locally initialized null Bytes.
let status =
unsafe { poq_generate_witness(std::ptr::from_ref(ffi_input), &raw mut ffi_output_bytes) };
status.try_into().map(|()| Witness::from(ffi_output_bytes))
}
pub fn generate_witness_from_files(dat: &Path, inputs: &Path, output: &Path) -> Result<(), Error> {
let c_dat = path_as_null_terminated_string(dat)?;
let c_inputs = path_as_null_terminated_string(inputs)?;
let c_output = path_as_null_terminated_string(output)?;
// SAFETY: c_dat, c_inputs, and c_output are valid null-terminated C strings for the duration of the call.
unsafe { poq_generate_witness_from_files(c_dat.as_ptr(), c_inputs.as_ptr(), c_output.as_ptr()) }
.try_into()
}
#[cfg(test)]
mod tests {
use super::{PoqWitnessInput, generate_witness, generate_witness_from_files};
use std::path::PathBuf;
use std::sync::LazyLock;
static LIB_DIR: LazyLock<PathBuf> = LazyLock::new(|| {
const ENV_VAR: &str = "LBC_POQ_LIB_DIR";
PathBuf::from(
std::env::var(ENV_VAR).unwrap_or_else(
|_| panic!("Environment variable '{ENV_VAR}' must be available, as provided by the build script."),
)
)
});
static INPUTS: LazyLock<PathBuf> =
LazyLock::new(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json"));
#[test]
fn test_generate_witness() {
let dat = LIB_DIR.join("witness_generator");
let witness_output_path = std::env::temp_dir().join("poq_test_witness.wtns");
generate_witness_from_files(&dat, &INPUTS, &witness_output_path)
.expect("generate_witness_from_files failed.");
let inputs_json = std::fs::read_to_string(&*INPUTS)
.unwrap_or_else(|_| panic!("Failed to read {}.", INPUTS.display()));
let input = PoqWitnessInput::new(inputs_json)
.expect("Failed to construct the input for the witness generator.");
let output = generate_witness(&input).expect("generate_witness failed.");
let expected = std::fs::read(&witness_output_path).unwrap_or_else(|_| {
panic!(
"Failed to read the generated witness from {}.",
witness_output_path.display()
)
});
assert_eq!(output.as_ref().iter().as_slice(), expected.as_slice());
}
}

View File

@ -0,0 +1,23 @@
[package]
name = "logos-blockchain-circuits-signature-sys"
categories.workspace = true
description.workspace = true
edition.workspace = true
keywords.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
version.workspace = true
[features]
prebuilt = ["lbc-build/prebuilt"]
[lints]
workspace = true
[dependencies]
lbc-types = { workspace = true }
lbc-common = { workspace = true }
[build-dependencies]
lbc-build = { workspace = true }

View File

@ -0,0 +1,3 @@
fn main() {
lbc_build::build("signature", "LBC_SIGNATURE_LIB_DIR");
}

View File

@ -0,0 +1,37 @@
{
"secret_keys": [
"4098660825806322106084917185877010365998862351268353704832317909929107002180",
"2596177389886188281596978093959690371159914261946677318098645747664853545334",
"19330846001036806632630691788566327913635233354660529061620854887613210243052",
"13718551754506405197443815435437300347734571755567740886306457364649402186553",
"6204148614304430226861132556010829917345597652038502840597675273498043144451",
"15755521481655998437707437556070134762137394314366394920968545131987543981102",
"11434634292101129704779207098617082796007566344360047961971399449469942728547",
"1091020027659842297575459412975398408818486151826195655315644888474716868332",
"1661418073407206326237403014548147762215859087746801732040113509361531462500",
"20628813409264227566051422798099856805018133740192165327801956537938406198081",
"327965494390850983495190187405149071612762485615773297559993697718924431952",
"8359983131932599018580929850473426740353652682335586902534879079235060354372",
"6001997642641265677487124391062970262114562733679341855865413164728827534876",
"4322142471707950388783633694420765022063880822490308569932169051464371677687",
"5199317950973283137516788124063301811586747068671537026189099053918194655518",
"4878043903155423406402270755752662933424125800765439117197056070420025827007",
"18383023798211595062002947539653067956198613952091768346565349157965661370879",
"16785909593043203210549043820115025237749244647904538613296130098899332712430",
"15467980282061694469300695749385517926850361484529888884367690782358434467841",
"10874762142964436895350252025677859023454508059151894039076338836998263676047",
"13448584667305124010536281145122646499463567394753279948588189274394145173068",
"10163232510156788884730739506862867831894904471198701058400872517735368970791",
"5220666930634917579087865848579036529755356054528301534514932964322747989260",
"19950242287192592205940905658928387310191814871825582446129402931984520537085",
"11493441223791405874036262554540342214352159322279505543922710193330606461053",
"13970669468715193555525716515867031876612509505528078470569781677204432503054",
"2617218434049077957942178980979194560061213987929479929629616784596361883755",
"10262860452995012573731922300160498681285885199421850052844703904968888647633",
"21818248483534286893890935091845502181077194830723832321102213680624411710883",
"7587080455062894070835842475066387476154828963596866773497564550910530217948",
"2067356816097510189886893732466232519758829910334449764538259630587857195500",
"582466643052618756521674522356117975734709415984615399233198386186277496483"
],
"msg": "15376625227674870759661626327809807256501138528181290032321552536459910806161"
}

View File

@ -0,0 +1,12 @@
use lbc_types::ffi::{Bytes, Status, WitnessInput};
use std::ffi::c_char;
unsafe extern "C" {
pub fn signature_generate_witness(input: *const WitnessInput, output: *mut Bytes) -> Status;
pub fn signature_generate_witness_from_files(
dat: *const c_char,
inputs: *const c_char,
output: *const c_char,
) -> Status;
}

View File

@ -0,0 +1,4 @@
mod ffi;
pub mod native;
pub use native::{SignatureWitnessInput, generate_witness, generate_witness_from_files};

View File

@ -0,0 +1,87 @@
use crate::ffi::{signature_generate_witness, signature_generate_witness_from_files};
use lbc_common::string::path_as_null_terminated_string;
use lbc_types::{
ffi,
native::{Error, Witness},
};
use std::path::Path;
static RAW_CIRCUIT_DAT: &[u8] = include_bytes!(concat!(
env!("LBC_SIGNATURE_LIB_DIR"),
"/witness_generator.dat"
));
pub struct SignatureDat;
impl<'dat> lbc_types::CircuitDat<'dat> for SignatureDat {
const DAT: &'dat [u8] = RAW_CIRCUIT_DAT;
}
pub type SignatureWitnessInput<'dat> = lbc_types::CircuitWitnessInput<'dat, SignatureDat>;
pub fn generate_witness(input: &SignatureWitnessInput) -> Result<Witness, Error> {
let ffi_input_guard = input.as_ffi();
let ffi_input = ffi_input_guard.as_ref();
let mut ffi_output_bytes = ffi::Bytes::null();
// SAFETY: ffi_input is a valid pointer and ffi_output_bytes is a locally initialized null Bytes.
let status = unsafe {
signature_generate_witness(std::ptr::from_ref(ffi_input), &raw mut ffi_output_bytes)
};
status.try_into().map(|()| Witness::from(ffi_output_bytes))
}
pub fn generate_witness_from_files(dat: &Path, inputs: &Path, output: &Path) -> Result<(), Error> {
let c_dat = path_as_null_terminated_string(dat)?;
let c_inputs = path_as_null_terminated_string(inputs)?;
let c_output = path_as_null_terminated_string(output)?;
// SAFETY: c_dat, c_inputs, and c_output are valid null-terminated C strings for the duration of the call.
unsafe {
signature_generate_witness_from_files(c_dat.as_ptr(), c_inputs.as_ptr(), c_output.as_ptr())
}
.try_into()
}
#[cfg(test)]
mod tests {
use super::{SignatureWitnessInput, generate_witness, generate_witness_from_files};
use std::path::PathBuf;
use std::sync::LazyLock;
static LIB_DIR: LazyLock<PathBuf> = LazyLock::new(|| {
const ENV_VAR: &str = "LBC_SIGNATURE_LIB_DIR";
PathBuf::from(
std::env::var(ENV_VAR).unwrap_or_else(
|_| panic!("Environment variable '{ENV_VAR}' must be available, as provided by the build script."),
)
)
});
static INPUTS: LazyLock<PathBuf> =
LazyLock::new(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("sample.input.json"));
#[test]
fn test_generate_witness() {
let dat = LIB_DIR.join("witness_generator");
let witness_output_path = std::env::temp_dir().join("signature_test_witness.wtns");
generate_witness_from_files(&dat, &INPUTS, &witness_output_path)
.expect("generate_witness_from_files failed.");
let inputs_json = std::fs::read_to_string(&*INPUTS)
.unwrap_or_else(|_| panic!("Failed to read {}.", INPUTS.display()));
let input = SignatureWitnessInput::new(inputs_json)
.expect("Failed to construct the input for the witness generator.");
let output = generate_witness(&input).expect("generate_witness failed.");
let expected = std::fs::read(&witness_output_path).unwrap_or_else(|_| {
panic!(
"Failed to read the generated witness from {}.",
witness_output_path.display()
)
});
assert_eq!(output.as_ref().iter().as_slice(), expected.as_slice());
}
}

View File

@ -0,0 +1,72 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "libc"
version = "0.2.185"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f"
[[package]]
name = "logos-blockchain-circuits-types"
version = "0.1.0"
dependencies = [
"libc",
"thiserror",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"

View File

@ -0,0 +1,17 @@
[package]
name = "logos-blockchain-circuits-types"
categories.workspace = true
description.workspace = true
edition.workspace = true
keywords.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
version.workspace = true
[lints]
workspace = true
[dependencies]
bytes = { workspace = true }
libc = { workspace = true }

View File

@ -0,0 +1,64 @@
use libc::free;
mod inner {
#[repr(C)]
pub struct Buffer<T> {
pub data: T,
pub size: usize,
}
}
impl<T> inner::Buffer<*const T> {
pub const fn null() -> Self {
Self {
data: std::ptr::null(),
size: 0,
}
}
}
impl<T> inner::Buffer<*mut T> {
pub const fn null() -> Self {
Self {
data: std::ptr::null_mut(),
size: 0,
}
}
}
/// Owned byte buffer returned by the C witness generator functions.
///
/// The inner `data` pointer must be null-initialized. It's heap-allocated by the C side and must be
/// freed with [`free_bytes`] after use.
pub type Bytes = inner::Buffer<*mut u8>;
/// Read-only byte slice passed into the C witness generator functions.
pub type ConstBytes = inner::Buffer<*const u8>;
/// Frees the data buffer inside a [`Bytes`] struct allocated by the C API.
///
/// Only the inner data buffer is freed, not the struct itself, since the latter is managed by the
/// caller.
///
/// # Arguments
///
/// - `bytes`: A pointer to a [`Bytes`] struct whose data buffer was allocated by the C API and
/// needs to be freed.
///
/// # Safety
///
/// Dereferences raw pointers.
pub unsafe fn free_bytes(bytes: *mut Bytes) {
if bytes.is_null() {
return;
}
// SAFETY: `bytes` is non-null (checked above).
let bytes = unsafe { &mut *bytes };
if !bytes.data.is_null() {
// SAFETY: `bytes.data` is non-null (checked above).
unsafe { free(bytes.data.cast::<libc::c_void>()) };
}
bytes.data = std::ptr::null_mut();
bytes.size = 0;
}

View File

@ -0,0 +1,12 @@
//! Raw `#[repr(C)]` types that mirror the C witness generator API.
//!
//! These types map directly to the C header structs and are used at the FFI boundary. Prefer the
//! wrappers in [`crate::native`] for ordinary Rust code.
pub mod bytes;
pub mod status;
pub mod witness_input;
pub use bytes::{Bytes, ConstBytes, free_bytes};
pub use status::Status;
pub use witness_input::WitnessInput;

View File

@ -0,0 +1,55 @@
use std::ffi::c_char;
/// Status codes for C API functions.
#[derive(PartialEq, Eq)] // Enables comparisons with named constants.
#[repr(transparent)]
pub struct Code(pub i32);
impl Code {
pub const OK: Self = Self(0);
pub const DYN_ERROR: Self = Self(1);
pub const INVALID_INPUT: Self = Self(2);
pub const OUT_OF_MEMORY: Self = Self(3);
#[must_use]
pub fn is_ok(&self) -> bool {
self == &Self::OK
}
#[must_use]
pub fn is_error(&self) -> bool {
!self.is_ok()
}
}
/// Status reporting structure for C API functions.
#[repr(C)]
pub struct Status {
pub code: Code,
pub message: [c_char; 256],
}
impl Status {
#[must_use]
pub const fn ok() -> Self {
Self {
code: Code::OK,
message: [0; 256],
}
}
#[must_use]
pub fn is_ok(&self) -> bool {
self.code.is_ok()
}
#[must_use]
pub fn is_error(&self) -> bool {
self.code.is_error()
}
#[must_use]
pub const fn has_message(&self) -> bool {
self.message[0] != 0
}
}

View File

@ -0,0 +1,13 @@
use crate::ffi::ConstBytes;
use std::ffi::c_char;
/// Input to a witness generator function.
///
/// Both pointers must remain valid for the duration of the C call.
#[repr(C)]
pub struct WitnessInput {
/// The circuit data file contents.
pub dat: ConstBytes,
/// Null-terminated JSON string containing the circuit inputs.
pub inputs_json: *const c_char,
}

View File

@ -0,0 +1,10 @@
//! Raw FFI types and Rust-safe wrappers for the witness generator C API.
//!
//! The [`ffi`] module contains `#[repr(C)]` types that mirror the C headers directly. The
//! [`native`] module re-exposes those through idiomatic Rust types that own their memory and
//! convert FFI return values into [`Result`]s.
pub mod ffi;
pub mod native;
pub use native::{CircuitDat, CircuitWitnessInput, Error};

View File

@ -0,0 +1,30 @@
use crate::native::{Error, WitnessInput};
use std::marker::PhantomData;
use std::ops::Deref;
pub trait CircuitDat<'dat> {
const DAT: &'dat [u8];
}
pub struct CircuitWitnessInput<'dat, Dat> {
inner: WitnessInput<'dat>,
_phantom: PhantomData<Dat>,
}
impl<'dat, Dat: CircuitDat<'dat>> CircuitWitnessInput<'dat, Dat> {
pub fn new(inputs_json: String) -> Result<Self, Error> {
let inner = WitnessInput::new(Dat::DAT, inputs_json)?;
Ok(Self {
inner,
_phantom: PhantomData,
})
}
}
impl<'dat, Dat> Deref for CircuitWitnessInput<'dat, Dat> {
type Target = WitnessInput<'dat>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}

View File

@ -0,0 +1,13 @@
//! Rust-safe wrappers around the FFI types.
//!
//! Use them in preference to the types in [`crate::ffi`].
pub mod circuit_witness_input;
pub mod status;
pub mod witness;
pub mod witness_input;
pub use circuit_witness_input::{CircuitDat, CircuitWitnessInput};
pub use status::{Error, Result};
pub use witness::Witness;
pub use witness_input::WitnessInput;

View File

@ -0,0 +1,52 @@
use crate::ffi::status::Code as FfiStatusCode;
use std::ffi::CStr;
use std::fmt::Display;
pub type Result<T> = std::result::Result<T, Error>;
/// Error returned when a witness generator call does not succeed.
#[derive(Debug)]
pub enum Error {
InvalidInput(Option<String>),
OutOfMemory(Option<String>),
Other(Option<String>),
}
impl std::error::Error for Error {}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (kind, message) = match self {
Self::InvalidInput(msg) => ("Invalid input", msg),
Self::OutOfMemory(msg) => ("Out of memory", msg),
Self::Other(msg) => ("Other error", msg),
};
match message {
Some(message) => write!(f, "{kind}: {message}"),
None => write!(f, "{kind}"),
}
}
}
impl TryFrom<crate::ffi::Status> for () {
type Error = Error;
fn try_from(status: crate::ffi::Status) -> Result<()> {
let message: Option<String> = status.has_message().then(|| {
// SAFETY: `status.message` is non-empty (checked by `has_message()`) and null-terminated as guaranteed by the C API.
let status_message = unsafe { CStr::from_ptr(status.message.as_ptr()) };
status_message.to_string_lossy().into_owned()
});
match status.code {
FfiStatusCode::OK => Ok(()),
FfiStatusCode::DYN_ERROR => Err(Error::Other(message)),
FfiStatusCode::INVALID_INPUT => Err(Error::InvalidInput(message)),
FfiStatusCode::OUT_OF_MEMORY => Err(Error::OutOfMemory(message)),
other => Err(Error::Other(Some(format!(
"Unknown status code: {}",
other.0
)))),
}
}
}

View File

@ -0,0 +1,34 @@
use crate::ffi;
/// Byte buffer
///
/// When constructing from [`From<ffi::Bytes>`], it takes ownership of the underlying value and
/// frees it.
pub struct Witness(bytes::Bytes);
impl From<bytes::Bytes> for Witness {
fn from(bytes: bytes::Bytes) -> Self {
Self(bytes)
}
}
impl AsRef<bytes::Bytes> for Witness {
fn as_ref(&self) -> &bytes::Bytes {
&self.0
}
}
impl From<ffi::Bytes> for Witness {
fn from(mut ffi_value: ffi::Bytes) -> Self {
let vec = if ffi_value.size == 0 || ffi_value.data.is_null() {
Vec::new()
} else {
// SAFETY: `ffi_value.data` is non-null and `ffi_value.size > 0` (checked above),
// pointing to a valid C-allocated buffer of at least `size` bytes.
unsafe { std::slice::from_raw_parts(ffi_value.data, ffi_value.size).to_vec() }
};
// SAFETY: `ffi_value` is a local variable, so the raw pointer is valid for this call.
unsafe { ffi::free_bytes(&raw mut ffi_value) };
Self::from(bytes::Bytes::from(vec))
}
}

View File

@ -0,0 +1,55 @@
use crate::ffi;
use crate::native::Error;
use std::ffi::CString;
/// Input for witness generators
pub struct WitnessInput<'dat> {
/// The circuit's dat file contents.
dat: &'dat [u8],
/// The JSON string containing the circuit inputs.
inputs_json: CString,
}
impl<'dat> WitnessInput<'dat> {
pub fn new(dat: &'dat [u8], inputs_json: String) -> Result<Self, Error> {
let inputs_json = CString::new(inputs_json).map_err(|error| {
Error::InvalidInput(Some(format!(
"The parameter inputs_json could not be converted to C string: {error}"
)))
})?;
Ok(Self { dat, inputs_json })
}
/// Borrows this value as a temporary FFI-compatible view.
#[must_use]
pub fn as_ffi(&'_ self) -> WitnessInputFfiGuard<'_> {
WitnessInputFfiGuard::new(self)
}
}
/// Temporary FFI view of a [`WitnessInput`], which makes [`ffi::WitnessInput`] lifetime-aware.
pub struct WitnessInputFfiGuard<'dat> {
ffi: ffi::WitnessInput,
_lifetime: std::marker::PhantomData<&'dat WitnessInput<'dat>>,
}
impl<'dat> WitnessInputFfiGuard<'dat> {
fn new(inner: &'dat WitnessInput) -> Self {
let dat = ffi::ConstBytes {
data: inner.dat.as_ptr(),
size: inner.dat.len(),
};
let inputs_json = inner.inputs_json.as_ptr();
let ffi = ffi::WitnessInput { dat, inputs_json };
Self {
ffi,
_lifetime: std::marker::PhantomData,
}
}
}
impl AsRef<ffi::WitnessInput> for WitnessInputFfiGuard<'_> {
fn as_ref(&self) -> &ffi::WitnessInput {
&self.ffi
}
}

191
src/circom_adapter.cpp Normal file
View File

@ -0,0 +1,191 @@
#include "circom_adapter.hpp"
#include "circom_fwd.hpp"
#include <vector>
#include <sstream>
#include <stdexcept>
Circom_Circuit* loadCircuit(const ConstBytes& circuit_bytes) {
Circom_Circuit* circuit = new Circom_Circuit;
circuit->InputHashMap = new HashSignalInfo[get_size_of_input_hashmap()];
uint dsize = get_size_of_input_hashmap() * sizeof(HashSignalInfo);
memcpy((void*)(circuit->InputHashMap), (void*)circuit_bytes.data, dsize);
circuit->witness2SignalList = new u64[get_size_of_witness()];
uint inisize = dsize;
dsize = get_size_of_witness() * sizeof(u64);
memcpy((void*)(circuit->witness2SignalList), (void*)(circuit_bytes.data + inisize), dsize);
circuit->circuitConstants = new FrElement[get_size_of_constants()];
if (get_size_of_constants() > 0) {
inisize += dsize;
dsize = get_size_of_constants() * sizeof(FrElement);
memcpy((void*)(circuit->circuitConstants), (void*)(circuit_bytes.data + inisize), dsize);
}
std::map<u32, IOFieldDefPair> templateInsId2IOSignalInfo1;
IOFieldDefPair* busInsId2FieldInfo1 = nullptr;
if (get_size_of_io_map() > 0) {
u32 index[get_size_of_io_map()];
inisize += dsize;
dsize = get_size_of_io_map() * sizeof(u32);
memcpy((void*)index, (void*)(circuit_bytes.data + inisize), dsize);
inisize += dsize;
assert(inisize % sizeof(u32) == 0);
assert(circuit_bytes.size % sizeof(u32) == 0);
u32 dataiomap[(circuit_bytes.size - inisize) / sizeof(u32)];
memcpy((void*)dataiomap, (void*)(circuit_bytes.data + inisize), circuit_bytes.size - inisize);
u32* pu32 = dataiomap;
for (int i = 0; i < get_size_of_io_map(); i++) {
u32 n = *pu32;
IOFieldDefPair p;
p.len = n;
IOFieldDef defs[n];
pu32 += 1;
for (u32 j = 0; j < n; j++) {
defs[j].offset = *pu32;
u32 len = *(pu32 + 1);
defs[j].len = len;
defs[j].lengths = new u32[len];
memcpy((void*)defs[j].lengths, (void*)(pu32 + 2), len * sizeof(u32));
pu32 += len + 2;
defs[j].size = *pu32;
defs[j].busId = *(pu32 + 1);
pu32 += 2;
}
p.defs = (IOFieldDef*)calloc(p.len, sizeof(IOFieldDef));
for (u32 j = 0; j < p.len; j++) {
p.defs[j] = defs[j];
}
templateInsId2IOSignalInfo1[index[i]] = p;
}
busInsId2FieldInfo1 = (IOFieldDefPair*)calloc(get_size_of_bus_field_map(), sizeof(IOFieldDefPair));
for (int i = 0; i < get_size_of_bus_field_map(); i++) {
u32 n = *pu32;
IOFieldDefPair p;
p.len = n;
IOFieldDef defs[n];
pu32 += 1;
for (u32 j = 0; j < n; j++) {
defs[j].offset = *pu32;
u32 len = *(pu32 + 1);
defs[j].len = len;
defs[j].lengths = new u32[len];
memcpy((void*)defs[j].lengths, (void*)(pu32 + 2), len * sizeof(u32));
pu32 += len + 2;
defs[j].size = *pu32;
defs[j].busId = *(pu32 + 1);
pu32 += 2;
}
p.defs = (IOFieldDef*)calloc(10, sizeof(IOFieldDef));
for (u32 j = 0; j < p.len; j++) {
p.defs[j] = defs[j];
}
busInsId2FieldInfo1[i] = p;
}
}
circuit->templateInsId2IOSignalInfo = move(templateInsId2IOSignalInfo1);
circuit->busInsId2FieldInfo = busInsId2FieldInfo1;
return circuit;
}
void loadJson(Circom_CalcWit *ctx, const char* inputs_json) {
json jin = json::parse(inputs_json);
json j;
//std::cout << jin << std::endl;
std::string prefix = "";
qualify_input(prefix, jin, j);
//std::cout << j << std::endl;
u64 nItems = j.size();
// printf("Items : %llu\n",nItems);
if (nItems == 0){
ctx->tryRunCircuit();
}
for (json::iterator it = j.begin(); it != j.end(); ++it) {
// std::cout << it.key() << " => " << it.value() << '\n';
u64 h = fnv1a(it.key());
std::vector<FrElement> v;
json2FrElements(it.value(),v);
uint signalSize = ctx->getInputSignalSize(h);
if (v.size() < signalSize) {
std::ostringstream errStrStream;
errStrStream << "Error loading signal " << it.key() << ": Not enough values\n";
throw std::runtime_error(errStrStream.str() );
}
if (v.size() > signalSize) {
std::ostringstream errStrStream;
errStrStream << "Error loading signal " << it.key() << ": Too many values\n";
throw std::runtime_error(errStrStream.str() );
}
for (uint i = 0; i<v.size(); i++){
try {
// std::cout << it.key() << "," << i << " => " << Fr_element2str(&(v[i])) << '\n';
ctx->setInputSignal(h,i,v[i]);
} catch (std::runtime_error e) {
std::ostringstream errStrStream;
errStrStream << "Error setting signal: " << it.key() << "\n" << e.what();
throw std::runtime_error(errStrStream.str() );
}
}
}
}
void writeBinWitness(Circom_CalcWit *ctx, Bytes* output_witness) {
std::vector<uint8_t> buf;
auto write = [&](const void* data, size_t size) {
const uint8_t* p = (const uint8_t*)data;
buf.insert(buf.end(), p, p + size);
};
write("wtns", 4);
u32 version = 2;
write(&version, 4);
u32 nSections = 2;
write(&nSections, 4);
// Header
u32 idSection1 = 1;
write(&idSection1, 4);
u32 n8 = Fr_N64*8;
u64 idSection1length = 8 + n8;
write(&idSection1length, 8);
write(&n8, 4);
write(Fr_q.longVal, Fr_N64*8);
uint Nwtns = get_size_of_witness();
u32 nVars = (u32)Nwtns;
write(&nVars, 4);
// Data
u32 idSection2 = 2;
write(&idSection2, 4);
u64 idSection2length = (u64)n8*(u64)Nwtns;
write(&idSection2length, 8);
FrElement v;
for (int i=0;i<Nwtns;i++) {
ctx->getWitness(i, &v);
Fr_toLongNormal(&v, &v);
write(v.longVal, Fr_N64*8);
}
size_t size = buf.size();
output_witness->data = static_cast<uint8_t*>(malloc(size));
if (output_witness->data == nullptr) return;
output_witness->size = size;
memcpy(output_witness->data, buf.data(), size);
}

13
src/circom_adapter.hpp Normal file
View File

@ -0,0 +1,13 @@
#ifndef CIRCOM_ADAPTER_HPP
#define CIRCOM_ADAPTER_HPP
#include "types.hpp"
#include "calcwit.hpp"
#include "circom.hpp"
// Return value
Circom_Circuit* loadCircuit(const ConstBytes& circuit);
void loadJson(Circom_CalcWit *ctx, const char* inputs_json);
void writeBinWitness(Circom_CalcWit *ctx, Bytes* output_witness);
#endif

30
src/circom_fwd.hpp Normal file
View File

@ -0,0 +1,30 @@
#ifndef CIRCOM_FWD_HPP
#define CIRCOM_FWD_HPP
/// Forward declarations for symbols defined in circom-generated main.cpp.
///
/// Circom compiles each circuit into a self-contained main.cpp that defines the witness generation
/// logic alongside several helper functions.
/// This header exposes those symbols so that FFI code can call into them without pulling in the full circom source.
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include "calcwit.hpp"
#include "circom.hpp"
#include "fr.hpp"
using json = nlohmann::json;
/// Forward declaration of circom main(). Renamed via -Dmain=circom_main to avoid UB.
/// TODO: Successful path has no explicit return.
int circom_main(int argc, char* argv[]);
bool check_valid_number(std::string& s, uint base);
void json2FrElements(json val, std::vector<FrElement>& 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);
#endif

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/poq/ffi.cpp Normal file
View File

@ -0,0 +1,119 @@
#include "poq/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 poq_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 poq_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/poq/ffi.hpp Normal file
View File

@ -0,0 +1,42 @@
#ifndef FFI_POQ_HPP
#define FFI_POQ_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 poq_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 poq_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

92
src/types.hpp Normal file
View File

@ -0,0 +1,92 @@
#ifndef TYPES_HPP
#define TYPES_HPP
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define STATUS_MESSAGE_LENGTH 256
#ifdef __cplusplus
extern "C" {
#endif
/// Mutable byte buffer.
typedef struct Bytes {
uint8_t* data;
size_t size;
} Bytes;
/// Immutable byte buffer.
typedef struct ConstBytes {
const uint8_t* data;
size_t size;
} ConstBytes;
typedef enum StatusCode {
StatusCode_Ok = 0,
StatusCode_DynError = 1,
StatusCode_InvalidInput = 2,
StatusCode_OutOfMemory = 3,
} StatusCode;
static inline bool status_code_is_ok(const StatusCode code) {
return code == StatusCode_Ok;
}
static inline 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;
char message[STATUS_MESSAGE_LENGTH];
} Status;
static inline Status status_new(const StatusCode code, const char* message) {
Status status = {code, {}};
if (message != NULL) {
strncpy(status.message, message, STATUS_MESSAGE_LENGTH - 1);
}
return status;
}
static inline Status status_from_code(const StatusCode code) {
return status_new(code, NULL);
}
static inline Status status_ok() {
return status_from_code(StatusCode_Ok);
}
static inline bool status_is_ok(const Status status) {
return status_code_is_ok(status.code);
}
static inline bool status_is_error(const Status status) {
return status_code_is_error(status.code);
}
/// Inputs for witness generation.
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;
/// Static inline is inlined at every call site and cannot be linked, so each language binding must reimplement it.
/// This is the best-effort approach for now.
/// TODO: Make this an exported symbol so Rust (and other callers) can link against a single canonical definition.
static inline void free_bytes(Bytes* bytes) {
if (bytes == NULL)
return;
free(bytes->data);
bytes->data = NULL;
bytes->size = 0;
}
#ifdef __cplusplus
}
#endif
#endif // TYPES_HPP