docs: Add documentation (#32)

This commit is contained in:
Álex 2026-05-29 12:13:44 +02:00 committed by GitHub
parent 2e79ac3083
commit cc41762cb5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 467 additions and 119 deletions

3
.markdownlint.json Normal file
View File

@ -0,0 +1,3 @@
{
"MD013": { "line_length": 120, "tables": false }
}

View File

@ -37,6 +37,11 @@ repos:
hooks:
- id: cargo-machete
args: ["rust/"]
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: e72a3ca1632f0b11a07d171449fe447a7ff6795e # v0.48.0
hooks:
- id: markdownlint
args: ["-f"]
- repo: local
hooks:
- id: cargo-hack-check

70
CHANGELOG.md Normal file
View File

@ -0,0 +1,70 @@
# Changelog
All notable changes to this project will be documented in this file.
## [0.5.0] — 2026-05-25
### Added
- **Circuits as static libraries** (#16)
The central change in this release. Previously, witness generation required spawning a compiled executable per circuit.
Each circuit is now a linkable static archive (`lib{circuit}.a`) with a stable C FFI.
Two entry points per circuit:
- `{circuit}_generate_witness(WitnessInput*, Bytes*)`: Generates a witness in memory from a
JSON input string and an embedded `.dat` buffer; the caller owns the output buffer.
- `{circuit}_generate_witness_from_files(dat, inputs, output)`: Generates a witness from a `.dat` file and a JSON
input file, writing the witness to an output file.
The Rust crates (`lbc-{circuit}-sys`) wrap this FFI directly and can either link against a local build or download a
prebuilt release automatically.
See [`rust/README.md`](rust/README.md) for usage.
- **Bundled static GMP** (#19)
`libgmp` is now compiled from source and statically linked, removing the runtime dependency on a system GMP
installation, and standardizing the GMP version used across all platforms.
- **Older glibc compatibility** (#21)
Linux builds now target an older glibc ABI for broader distribution support.
- **Dual MIT/Apache-2.0 license**.
### Fixed
- **Symbol resolution conflicts** (#22, #28, #29)
When multiple circuit libraries are linked into the same binary, the linker silently collapsed shared internal
symbols to a single definition, corrupting witness parsing and causing SIGSEGV.
See [CONTRIBUTING.md](CONTRIBUTING.md#symbol-isolation-in-circuit-libraries) for a full explanation and maintenance
notes.
- **macOS C++ library linkage** (#27)
Fixed missing C++ standard library on macOS builds.
- **Circuit assert statements** (#26)
Assert statements in the circuit source assumed standalone binary execution, causing disruptions when compiled as a library.
- **Comparator full less-than**
Fixed incorrect output in the full less-than comparator circuit.
### Housekeeping
- Auto-update Nix hashes on release (#25)
- Incremental builds (#20)
- Pinned CI action versions (#24)
- `rustfmt` configuration (#23)
- GMP version consistency (#31)
- String formatting fix (#30)
---
*No changelog was kept prior to v0.5.0.*

View File

@ -4,21 +4,34 @@
### 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.
- `llvm-objcopy` — required to build circuit static libraries (symbol isolation). On macOS, install via `brew install llvm` (LLVM 20+ required).
#### Sys development
### Installing the Pre-Commit Hooks
- [Rust](https://rustup.rs/) — the pinned toolchain version is in `rust-toolchain.toml` and will be installed
automatically by `rustup`.
- Compiled circuit libraries (`.a` files and `witness_generator.dat`) — see [rust/README.md](rust/README.md) for how to
provide them.
#### Building circuits
- `llvm-objcopy` — required for symbol isolation when building circuit static libraries. On macOS, install via `brew
install llvm` (LLVM 20+ required).
### Pre-Commit
[pre-commit](https://pre-commit.com/) covers most of the lints required by CI. It's not mandatory — you can run checks
however you like — but it's the easiest way to catch issues before pushing.
#### Installation
```bash
pre-commit install
```
This only needs to be done once after cloning the repo. Hooks will then run automatically on `git commit`.
After this, they will be run automatically on every commit.
### Running Checks Manually
#### Running Manually
To run all hooks manually against all files:
To run the checks manually against all files:
```bash
pre-commit run --all-files
@ -28,169 +41,135 @@ pre-commit run --all-files
#### 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).
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`)
---
## Symbol Isolation in Circuit Libraries
## Building
Each circuit (PoQ, PoL, PoC, Signature) is compiled into a static archive (`libpoq.a`, `libpol.a`, etc.).
All four archives share the same internal C++ runtime — `loadCircuit`, `get_size_of_witness`, the `fr_*` field
arithmetic functions, `calcwit_*` functions, and others. They are compiled from the same source files but with
**different constant values per circuit** (e.g. `get_size_of_witness()` returns 18149 for PoQ and 20531 for PoL).
For a full walkthrough of the CI build steps, from `.circom` source to release artifacts, see
[docs/build-pipeline.md](docs/build-pipeline.md).
### The Problem
### Symbol Isolation in Circuit Libraries
When two or more circuit libraries are linked into the same binary, the linker silently picks the first definition
it encounters for each symbol and discards the rest.
No error, no warning.
The result is that one circuit's constants end up hardwired into functions shared by both circuits, corrupting witness
parsing.
In practice: the wrong `get_size_of_witness()` value causes `loadCircuit` to compute an incorrect buffer size, `pu32`
#### The Problem
Each circuit (PoQ, PoL, PoC, Signature) is compiled into a static archive (`libpoq.a`, `libpol.a`, etc.).
All archives share the same symbols, compiled from the same source files but with **different
constant values per circuit** (e.g. `get_size_of_witness()` returns 18149 for PoQ and 20531 for PoL).
When two or more circuit libraries are linked into the same binary, the linker silently picks the first definition it
encounters for each symbol and discards the rest without any sort of error or warning.
The result is that one circuit's constants end up hardwired into functions shared by both circuits, corrupting witness
parsing.
In practice: the wrong `get_size_of_witness()` value causes `loadCircuit` to compute an incorrect buffer size, `pu32`
walks off the end of the buffer, reads garbage as a length field, and the subsequent `memcpy` reads past the stack guard
page, which results in a **SIGSEGV**.
### The Fix
#### The Fix
The Makefile's `$(LIB)` rule uses a two-step process to localize all circuit-specific code before archiving:
The Makefile uses a two-step process to hide all circuit-specific symbols before archiving:
1. **Partial link** (`ld -r`): merges all circuit-specific `.o` files — everything except `fr.o` (pure field
arithmetic, no circuit-specific calls) — into a single relocatable object. No symbols are resolved yet; this is
consolidation only.
2. **Symbol localization** (`llvm-objcopy --keep-global-symbol` / `objcopy --keep-global-symbol`): demotes every global symbol to local *except* the
circuit's two public FFI entry points (`$(PROJECT)_generate_witness` and `$(PROJECT)_generate_witness_from_files`).
Local symbols are invisible to the final linker, so each archive retains a private copy of every internal symbol — no
conflict is possible regardless of how many circuits are linked together.
1. **Partial link** (`ld -r`): merges all circuit-specific `.o` files into a single relocatable
object. `fr.o` is excluded — it contains only field arithmetic with no circuit-specific calls
and is safe to deduplicate across circuits.
2. **Symbol localization**: demotes every global symbol to local except the two public FFI entry
points (`$(PROJECT)_generate_witness` and `$(PROJECT)_generate_witness_from_files`). Local
symbols are invisible to the final linker, so each archive retains a private copy.
`llvm-objcopy` is required rather than GNU `objcopy`. GNU `objcopy` only changes the binding of COMDAT signature
symbols to local, which confuses the linker's deduplication logic and causes "relocation refers to symbol in discarded
section" errors. `llvm-objcopy` additionally clears the `GRP_COMDAT` flag on affected section groups, turning them into
regular non-COMDAT sections that are simply kept as-is rather than deduplicated. The result is a slightly larger binary
(each circuit keeps its own copy of shared template instantiations), but no linker errors.
**`llvm-objcopy` vs GNU `objcopy`**
`fr.o` is excluded from the merge because it contains only field arithmetic (`Fr_*`) with no circuit-specific calls.
It is safe to deduplicate across circuits — the linker picks one copy, which is correct since the code is identical.
`llvm-objcopy` is required on Linux. GNU `objcopy` only changes the binding of COMDAT signature
symbols to local, confusing the linker's deduplication logic and causing "relocation refers to
symbol in discarded section" errors. `llvm-objcopy` additionally clears the `GRP_COMDAT` flag,
turning affected sections into regular non-COMDAT sections. Slightly larger binary, no linker errors.
On macOS/Mach-O, `llvm-objcopy` (from `brew install llvm`) is used — it supports `--keep-global-symbol` for
Mach-O since LLVM 20. It is not available in Xcode's toolchain and must be installed separately.
Mach-O prepends an underscore to every C symbol (`poc_generate_witness``_poc_generate_witness`),
so `--keep-global-symbol` arguments must include the leading `_`. The Makefile's `SYM_PREFIX` variable
handles this: it is set to `_` on macOS and empty on other platforms.
##### macOS
On Windows, GNU `objcopy` (from MinGW binutils) is used instead of `llvm-objcopy`. `llvm-objcopy --keep-global-symbol`
is not supported for COFF objects, but GNU `objcopy --keep-global-symbol` works correctly on COFF — it maps the local
binding to COFF storage class `C_STAT`. The ELF `GRP_COMDAT` problem that required `llvm-objcopy` on Linux does not
apply on Windows: COFF COMDAT is per-section rather than group-based, and the linker already deduplicates it
automatically.
Uses `llvm-objcopy` (from `brew install llvm`, LLVM 20+).
### Maintenance
Mach-O prepends `_` to every C symbol, so `--keep-global-symbol` arguments must include the
leading `_`. The Makefile's `SYM_PREFIX` variable handles this automatically.
##### Windows
Uses GNU's `objcopy` (from MinGW binutils).
GNU's `objcopy` works correctly on COFF, mapping local binding to storage class `C_STAT`.
The ELF `GRP_COMDAT` problem doesn't apply: COFF COMDAT is per-section rather than group-based.
#### FFI Maintenance
`PUBLIC_SYMS` is hardcoded to `$(PROJECT)_generate_witness` and `$(PROJECT)_generate_witness_from_files` in the
Makefile. If the public FFI API ever changes — entry points renamed or new ones added — update that variable,
otherwise the affected symbols will be localized and linking will fail.
Makefile. If the public FFI API ever changes — entry points renamed or new ones added — update that variable, otherwise
the affected symbols will be localized and linking will fail.
---
## Triggering a New Release for Logos Blockchain Circuits
## Releasing
### Triggering a Release Build
To trigger a release build:
1. Create and push a tag in the format `vX.Y.Z`.
1. Create and push a tag in the format `vX.Y.Z`:
```bash
git tag v1.2.3 -m "Release v1.2.3"
git push --tags
```
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.
> Pull Requests will also generate artifacts, which may be found on the job's page, but won't generate a new release.
### Generated Artifacts
#### Generated artifacts
Each release includes a single unified bundle per platform:
For each supported platform (Linux x86_64, Linux aarch64, macOS aarch64, Windows x86_64), a release artifact is
generated:
#### Unified Release Bundles
For each supported platform (Linux x86_64, macOS aarch64, Windows x86_64):
- **`logos-blockchain-circuits-{version}-{os}-{arch}.tar.gz`**
A complete bundle containing all components needed to generate and verify proofs for all circuits.
**`logos-blockchain-circuits-{version}-{os}-{arch}.tar.gz`** — a complete bundle containing all components needed to
generate and verify proofs for all circuits.
**Bundle Structure:**
```
```text
logos-blockchain-circuits-{version}-{os}-{arch}/
├── VERSION
├── lib/
│ └── libgmp.a
├── {circuit}/ (poc/, pol/, poq/, signature/)
│ ├── include/
│ ├── lib{circuit}.a
│ ├── proving_key.zkey
│ ├── verification_key.json
│ └── witness_generator.dat
├── prover[.exe]
├── verifier[.exe]
├── pol/
│ ├── libpol.a
│ ├── witness_generator.dat
│ ├── include/
│ ├── proving_key.zkey
│ └── verification_key.json
├── poq/
│ ├── libpoq.a
│ ├── witness_generator.dat
│ ├── include/
│ ├── proving_key.zkey
│ └── verification_key.json
├── signature/
│ ├── libsignature.a
│ ├── witness_generator.dat
│ ├── include/
│ ├── proving_key.zkey
│ └── verification_key.json
└── poc/
├── libpoc.a
├── witness_generator.dat
├── include/
├── proving_key.zkey
└── verification_key.json
└── VERSION
```
> On Windows, static libraries use the `.lib` extension instead of `.a` (e.g. `pol.lib`).
> On Windows, static libraries use the `.lib` extension instead of `.a` (e.g. `pol.lib`, `gmp.lib`).
At the root level:
- **prover**: Rapidsnark prover binary for generating zk-SNARK proofs
- **verifier**: Rapidsnark verifier binary for verifying proofs
The proving keys are generated using the Hermez Powers of Tau ceremony — see [docs/build-pipeline.md § Step 2](docs/build-pipeline.md#step-2--proving-key-generation).
Each circuit directory contains:
- **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
### Publishing
The proving keys are generated using the Hermez Powers of Tau ceremony (`powersOfTau28_hez_final_17.ptau`), which supports circuits with up to 2^17 constraints.
### Example
```bash
git tag v1.2.3 -m "Release v1.2.3"
git push --tags
```
## Publishing the Release
After triggering the release, it will appear as a **Draft** and **Pre-Release**.
Before making it public, make sure to:
Releases are marked as **Draft** and **Pre-Release** to ensure the changelog and pre-release steps are manually reviewed
before going public. Before publishing:
1. **Review the changelog**
Ensure that all relevant changes are clearly listed and properly formatted.
2. **Confirm the pre-release checklist**
Verify that all required steps have been completed, then remove the checklist from the release notes.
Once everything looks good:
3. **Mark the release as published**
- Uncheck **“This is a pre-release.”**
- Publish the release (removing the Draft state).
> ⚡ **Important:** Logos Blockchain builds will only pick up the new circuits once the release is published as **Latest** (i.e. not marked as draft or pre-release).

View File

@ -1 +1,54 @@
# Logos Blockchain Circuits
ZK-SNARK circuits for the [Logos Blockchain](https://github.com/logos-blockchain/logos-blockchain), built with
[Circom](https://docs.circom.io/) and distributed as linkable static libraries.
## Circuits
| Circuit | What it proves |
|-------------------------------|-------------------------------------------------------------------|
| **PoQ** — Proof of Quota | A node has quota to participate in a blend session |
| **PoL** — Proof of Leadership | A note holder would win the leadership lottery for a given slot |
| **PoC** — Proof of Claim | A voucher is validly owned and its nullifier is correctly derived |
| **Signature** | Knowledge of secret keys and their corresponding public keys |
## Architecture
```mermaid
flowchart TD
A["Source circuit\n({circuit}.circom)"]
B("Circom compilation")
C["{circuit}_cpp/"]
R["{circuit}.r1cs"]
PK("Proving key generation")
Z["proving_key.zkey\nverification_key.json"]
DAT["{circuit}.dat"]
D["src/\n(circom_adapter, types, ...)"]
D2["src/{circuit}/ffi.cpp"]
E("Makefile")
F["lib{circuit}.a\nlibgmp.a"]
G["lbc-{circuit}-sys"]
A --> B -->|Generates| C
B -->|Generates| R --> PK --> Z
B -->|Generates| DAT
D -.-> C
D2 -.-> C
C --> E -->|Produces| F
F -->|Linked by| G
DAT -->|Embedded into| G
```
Each circuit is compiled from Circom source to C++, combined with shared common files (`circom_adapter`, `types`, ...)
and a circuit-specific FFI layer (`src/{circuit}/ffi.cpp`), and built into a static library.
The Rust sys crates link directly against these libraries.
## Docs
| Document | What's in it |
|--------------------------------------------------|------------------------------------------------------------|
| [CHANGELOG.md](CHANGELOG.md) | What changed and why, by version |
| [CONTRIBUTING.md](CONTRIBUTING.md) | Dev setup, build details, and release process |
| [rust/README.md](rust/README.md) | How to use the Rust sys crates |
| [docs/build-pipeline.md](docs/build-pipeline.md) | CI build steps, from `.circom` source to release artifacts |

141
docs/build-pipeline.md Normal file
View File

@ -0,0 +1,141 @@
# Build Pipeline
This document walks through the CI build steps, from `.circom` source to the full set of release artifacts.
---
## Overview
| Step | What happens |
|-----------------------------------------------------------------------------|---------------------------------------------------------------------|
| [1 — Circom compilation](#step-1--circom-compilation) | `.circom` source compiled to C++ |
| [2 — Proving key generation](#step-2--proving-key-generation) | `.r1cs` + `.ptau``.zkey` and `verification_key.json` |
| [3 — Patching `main.cpp`](#step-3--the-maincpp-return-patch) | Fix missing `return` to prevent UB infinite loop |
| [4 — The FFI layer](#step-4--the-ffi-layer) | Common files and circuit-specific FFI layered into `{circuit}_cpp/` |
| [5 — Compilation and linking](#step-5--compilation-and-linking) | Compile with symbol isolation |
| [6 — The `.dat` file](#step-6--the-dat-file) | Binary circuit data embedded in the Rust crate at compile time |
| [7 — GMP](#step-7--gmp) | Bundled static `libgmp.a` used instead of system GMP |
| [8 — Rust build script](#step-8--rust-build-script) | Resolves library paths, emits Cargo link directives |
| [9 — Rapidsnark](#step-9--rapidsnark) | Prover and verifier binaries built and bundled |
---
## Step 1 — Circom compilation
Produces three outputs:
- `{circuit}_cpp/` — C++ source files for the witness generator.
- `{circuit}.r1cs` — the constraint system, used for proving key generation.
- `{circuit}.dat` — binary circuit data.
---
## Step 2 — Proving key generation
The `.r1cs` from Step 1 is combined with the Hermez Powers of Tau ceremony file to produce:
- `proving_key.zkey` — Groth16 proving key, used by the prover to generate proofs.
- `verification_key.json` — used to verify proofs.
---
## Step 3 — The `main.cpp` return patch
Immediately after circom runs, the build patches `main.cpp` to insert a `return 0;` at the end of `main()`.
**Why**: The FFI layer calls circom's `main()` directly, which is already technically UB in C++ (calling `main` directly
is forbidden by the standard). On top of that, circom generates `main()` with no explicit `return` on the success path.
With `-O3`, the compiler treats the missing `return` as undefined behaviour and can optimise the entire success path
into an infinite loop.
---
## Step 4 — The FFI layer
Circom's generated C++ has no stable external API, the FFI layer adds one.
It consists of two groups of files copied into `{circuit}_cpp/` before compilation:
- **Common adapter files** (`circom_adapter`, `types`, `circom_fwd`, `assert.h`): Bridge the
circom internals to a stable C ABI, shared across all circuits.
- **Circuit-specific entry points** (`src/{circuit}/ffi.cpp`): The public `extern "C"` functions
that become the library's API.
```text
src/
types.hpp → copied into {circuit}_cpp/
circom_adapter.cpp → copied into {circuit}_cpp/
circom_adapter.hpp → copied into {circuit}_cpp/
circom_fwd.hpp → copied into {circuit}_cpp/
assert.h → copied into {circuit}_cpp/
{circuit}/
ffi.cpp → copied into {circuit}_cpp/{circuit}/
ffi.hpp → copied into {circuit}_cpp/{circuit}/
```
---
## Step 5 — Compilation and linking
### Compilation
All sources are compiled with `-Dmain=circom_main` (alongside standard flags), with bundled GMP
headers prepended before the system include path — see [Step 7](#step-7--gmp).
### Symbol isolation
All circuits compile the same internal functions (`loadCircuit`, `get_size_of_witness`, etc.) from the same source, but
with different circuit-specific constants. When multiple circuits are linked into the same binary, the linker silently
picks one definition per symbol and discards the rest, mixing constants across circuits and corrupting witness
generation.
To prevent this, every internal symbol is hidden: circuit objects are merged into a single
relocatable object, then all symbols except the two public entry points are demoted to local.
Local symbols are invisible to the final linker, so each circuit keeps its own private copy.
`fr.o` is the exception: it stays global and is added to the archive separately since it doesn't vary between circuits.
See [CONTRIBUTING.md § Symbol Isolation](../CONTRIBUTING.md#symbol-isolation-in-circuit-libraries) for the full
explanation and implementation details.
---
## Step 6 — The `.dat` file
`{circuit}.dat` (produced in Step 1) is embedded in the Rust crate at compile time.
---
## Step 7 — GMP
To standardise the GMP version used by all circuits, `libgmp.a` is built from source as part of
the CI build and placed in a `lib/` directory alongside the circuit artifacts. CI sets
include/link flags to point at the bundled GMP before any system path, ensuring it takes priority.
---
## Step 8 — Rust build script
Each `-sys` crate delegates its `build.rs` to `lbc_build`, which resolves the library paths,
links `lib{circuit}.a` and `libgmp.a` into Cargo, and re-exports the path to
`witness_generator.dat` for compile-time embedding. See [rust/README.md](../rust/README.md).
---
## Step 9 — Rapidsnark
The `prover` and `verifier` binaries are built from the rapidsnark submodule and bundled with
the release artifacts alongside the circuit libraries.
---
## Regenerating a circuit
If you change a `.circom` source file:
1. Recompile the circuit and rebuild the library.
2. Update the proving key if the R1CS changed — a changed `.circom` almost always changes the
R1CS, which invalidates the existing `.zkey`.
If you add a new public FFI entry point to `src/{circuit}/ffi.hpp`, update `PUBLIC_SYMS` in the
Makefile. Any symbol not listed there will be localised and the linker will fail to find it.

View File

@ -1,2 +1,99 @@
# Logos Blockchain Circuits Rust
This directory contains the Rust FFI to interact with the Logos Blockchain Circuits.
# Logos Blockchain Circuits — Rust
Rust bindings for the Logos Blockchain Circuits.
Each circuit has a `-sys` crate that wraps the underlying C FFI with a safe Rust API.
## Crates
| Crate | Description |
|---------------------|-------------------------------------------------|
| `lbc-poq-sys` | Witness generator for PoQ (Proof of Quota) |
| `lbc-pol-sys` | Witness generator for PoL (Proof of Leadership) |
| `lbc-poc-sys` | Witness generator for PoC (Proof of Claim) |
| `lbc-signature-sys` | Witness generator for Signature |
| `lbc-types` | Shared types |
| `lbc-build` | Build helper |
## Providing the circuit libraries
Each `-sys` crate needs the compiled circuit library directory for its circuit, containing `lib{circuit}.a` and `witness_generator.dat`.
There are two ways to provide it, detailed below.
### Option A — Prebuilt download
Enable the `prebuilt` Cargo feature.
The build script downloads the release bundle matching the crate version from GitHub Releases and caches it locally.
```toml
[dependencies]
lbc-poq-sys = { version = "0.5", features = ["prebuilt"] }
```
The cache location depends on the operating system:
- Linux: `~/.cache/logos/blockchain/`
- macOS: `~/Library/Caches/logos/blockchain/`
- Windows: `%LOCALAPPDATA%\logos\blockchain\`
The downloaded bundle version always matches `CARGO_PKG_VERSION`.
### Option B — Custom path
Point the build script at any directory containing the compiled circuit libraries:
```bash
LBC_POQ_LIB_DIR=/path/to/poq
LBC_POL_LIB_DIR=/path/to/pol
LBC_POC_LIB_DIR=/path/to/poc
LBC_SIGNATURE_LIB_DIR=/path/to/signature
LBC_LIB_DIR=/path/to/lib # Directory containing the required libs
# If using a release bundle, this is the included lib/ directory
```
> The `justfile` at the repo root contains recipes (`just poq`, `just pol`, etc.) that build the circuit libraries from
> source.
>
> This is not yet an officially supported workflow, but it can serve as a reference if you need to produce the
> libraries yourself.
## Usage
### In-memory witness generation
```rust
use lbc_poq_sys::native::{PoqWitnessInput, generate_witness};
use lbc_types::native::{Error, Witness};
fn main() -> Result<Witness, Error> {
let inputs_json = std::fs::read_to_string("poq-input.json").expect("failed to read input");
let input = PoqWitnessInput::new(inputs_json)?;
generate_witness(&input)
}
```
### File-based witness generation
```rust
use lbc_poq_sys::native::generate_witness_from_files;
use lbc_types::native::Error;
use std::path::Path;
fn main() -> Result<(), Error> {
generate_witness_from_files(
Path::new("/path/to/witness_generator"), // Extensionless .dat file
Path::new("poq-input.json"),
Path::new("output.wtns"),
)?;
Ok(())
}
```
The other circuits follow the same pattern under `lbc_pol_sys`, `lbc_poc_sys` and `lbc_signature_sys`.
## Running tests
```bash
cargo test
```