Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["crates/*", "crates/frontend/ceck"]
members = ["crates/*", "crates/frontend/ceck", "crates/prover-client"]
resolver = "2"

[workspace.package]
Expand Down
34 changes: 34 additions & 0 deletions crates/prover-client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[package]
name = "binius-prover-client"
version = "0.1.0"
edition = "2021"
authors = ["Irreducible Team <opensource@irreducible.com>"]
description = "Simplified open-source interface to the Binius ZK prover using serialization"
license = "MIT OR Apache-2.0"
build = "build.rs"

[lib]
# This is a Rust crate (rlib). The cdylib is only built for internal testing purposes.
crate-type = ["rlib", "cdylib"]

[dependencies]
# Core dependencies for serialization
binius-core = { path = "../core" }
binius-utils = { path = "../utils" }
bytes = "1.7"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workspace configuration requires all crates to reference dependencies declared in the workspace root Cargo.toml. The dependency bytes = "1.7" should be declared in the workspace root and referenced here as bytes.workspace = true.

Spotted by Diamond (based on custom rule: Irreducible Rust and Cargo)

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.


# Error handling
thiserror = "2.0"
anyhow = "1.0"

# Needed for the FFI implementation (only when building cdylib)
binius-prover = { path = "../prover", optional = true }

[features]
default = []
# Enable building the FFI implementation (for cdylib)
ffi-impl = ["binius-prover"]

[dev-dependencies]
# For testing and benchmarking
criterion = "0.6"
180 changes: 180 additions & 0 deletions crates/prover-client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# Binius Prover Client

A Rust crate providing a clean API for Binius ZK proof generation.

## Overview

This is a standard Rust library that provides a type-safe interface for generating Binius proofs. It uses FFI to communicate with the prover via serialized data, enabling the prover to be distributed as a closed-source binary while keeping the interface open-source.

## Architecture

```
┌─────────────────────┐ ┌─────────────────────┐ ┌──────────────────────┐
│ Your Rust App │────│ Prover Client │────│ Binius Prover │
│ │ │ (This Crate) │ │ (C Library) │
│ - ConstraintSystem │ │ - ProverClient │ │ - binius_prove() │
│ - ValuesData │ │ - Serialization │ │ - Proof generation │
│ - Proof │ │ - Error handling │ │ │
└─────────────────────┘ └─────────────────────┘ └──────────────────────┘
```

## Installation

Add this crate to your `Cargo.toml`:

```toml
[dependencies]
binius-prover-client = { path = "../prover-client" }
```

## Requirements

This crate requires the Binius prover to be available as a C library:

```bash
export BINIUS_PROVER_LIB_PATH=/path/to/prover/library
```

Without this, the crate will compile but return runtime errors when attempting to generate proofs.

## Usage

```rust
use binius_prover_client::ProverClient;
use binius_core::constraint_system::{ConstraintSystem, ValuesData};

// Create a prover client instance
let prover = ProverClient::new(1); // log_inv_rate = 1

// Generate proof from constraint system and witness data
let proof = prover.prove(&constraint_system, &public_witness, &private_witness)?;
```

The crate provides three API methods:
- `prove()` - Takes Rust types, handles serialization internally
- `prove_serialized()` - Takes pre-serialized bytes, returns deserialized `Proof`
- `prove_serialized_raw()` - Takes and returns raw bytes for maximum efficiency

## FFI Interface Details

The FFI boundary uses a single C function with serialized inputs/outputs:

```c
// Returns proof size on success, negative error code on failure
int32_t binius_prove(
const uint8_t* cs_bytes, // Serialized ConstraintSystem
size_t cs_len,
const uint8_t* pub_witness_bytes, // Serialized public ValuesData
size_t pub_witness_len,
const uint8_t* priv_witness_bytes,// Serialized private ValuesData
size_t priv_witness_len,
uint32_t log_inv_rate, // Proof generation parameter
uint8_t* proof_out, // Output buffer for serialized Proof
size_t proof_capacity // Size of output buffer
);
```

### Error Codes

- **Positive number**: Size of the proof written to `proof_out` (success)
- **-1**: Null pointer error
- **-2**: Invalid input data
- **-3**: Proving error
- **-4**: Serialization error
- **-5**: Output buffer too small

## Testing and Development

### Running the Test Suite

The crate includes a focused test suite with automatic FFI library management:

```bash
# Quick test - builds FFI library and runs all tests
./test_prover_client.sh
```

This script will:
1. Build the FFI library with the current implementation
2. Set up library paths automatically
3. Run integration tests for all API variants
4. Verify FFI boundary crossing works correctly

### Manual Testing

```bash
# Build the FFI library
cargo build --release --features ffi-impl

# Set library path and run tests
export BINIUS_PROVER_LIB_PATH=$(pwd)/target/release
cargo test
```

### Test Coverage

The test suite focuses on interface correctness:
- **API methods**: All three variants (`prove`, `prove_serialized`, `prove_serialized_raw`)
- **FFI boundary**: Verifies data crosses the FFI boundary correctly
- **Serialization**: Ensures proper serialization/deserialization
- **Trait implementation**: Tests Default trait and accessor methods

## Implementation Notes

### Library Detection

The crate's build script automatically detects the external prover library:

- Checks `BINIUS_PROVER_LIB_PATH` environment variable
- Sets up linking when library is found
- Provides graceful fallback when library is unavailable

### FFI Implementation

The file `src/ffi_impl.rs` contains the Binius prover wrapped in a C-compatible FFI interface. This is used to test the FFI boundary. In a closed-source deployment, this code would be compiled as a proprietary C library.

## Advanced Usage

### Error Handling

The interface provides detailed error information:

```rust
use binius_prover_client::{ProverClient, ProverError};

match prover.prove(&cs, &pub_witness, &priv_witness) {
Ok(proof) => println!("Proof generated: {} bytes", proof.data().len()),
Err(ProverError::LibraryNotAvailable(msg)) => {
eprintln!("FFI library not found: {}", msg);
// Handle library not available case
}
Err(ProverError::FfiError(code)) => {
eprintln!("FFI error code: {}", code);
// Handle specific FFI error codes
}
Err(e) => eprintln!("Other error: {}", e),
}
```

### Performance Considerations

- **Pre-serialized data**: Use `prove_serialized_raw()` when you already have serialized inputs
- **Library linking**: Dynamic linking adds minimal overhead compared to proof generation time
- **Memory management**: The FFI boundary uses copying; consider this for very large constraint systems

### Integration with Existing Code

The interface is designed to integrate easily with existing Binius workflows:

```rust
// Works with existing constraint system construction
let cs = constraint_system_builder.build();
let witness = witness_builder.build();

// Drop-in replacement for direct prover usage
let prover = ProverClient::new(log_inv_rate);
let proof = prover.prove(&cs.constraint_system, &witness.public, &witness.private)?;

// Use proof with existing verification code
verify_proof(&proof, &public_inputs)?;
```
82 changes: 82 additions & 0 deletions crates/prover-client/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use std::env;

Check warning on line 1 in crates/prover-client/build.rs

View workflow job for this annotation

GitHub Actions / pre-commit-arm64

Diff in /home/runner/work/monbijou/monbijou/crates/prover-client/build.rs

Check warning on line 1 in crates/prover-client/build.rs

View workflow job for this annotation

GitHub Actions / pre-commit-x86_64

Diff in /home/runner/work/monbijou/monbijou/crates/prover-client/build.rs
use std::path::{Path, PathBuf};

fn main() {
// Tell Cargo about our custom cfg flags
println!("cargo::rustc-check-cfg=cfg(has_binius_prover)");
println!("cargo::rustc-check-cfg=cfg(no_binius_prover)");

// Skip library detection when we're building the FFI implementation itself
// This avoids circular dependency when building as cdylib
if env::var("CARGO_FEATURE_FFI_IMPL").is_ok() {
println!("cargo:rustc-cfg=no_binius_prover");
return;
}

// Try to find the closed-source library via environment variable
if let Ok(path) = env::var("BINIUS_PROVER_LIB_PATH") {
let path = PathBuf::from(path);

if verify_library_exists(&path) {
// Set up linking
println!("cargo:rustc-link-search={}", path.display());
println!("cargo:rustc-link-lib=binius_prover");

// Store the library info for runtime access
println!("cargo:rustc-env=LINKED_BINIUS_LIB_PATH={}", path.display());
if let Some(file_name) = find_library_file(&path) {
println!("cargo:rustc-env=LINKED_BINIUS_LIB_NAME={}", file_name);
}

// Set a cfg flag to enable integration tests
println!("cargo:rustc-cfg=has_binius_prover");
} else {
println!("cargo:rustc-cfg=no_binius_prover");
}
} else {
// Library not found - this is OK for development
println!("cargo:rustc-cfg=no_binius_prover");
}

// Always rerun if these change
println!("cargo:rerun-if-env-changed=BINIUS_PROVER_LIB_PATH");
println!("cargo:rerun-if-changed=build.rs");
}

fn find_library_file(dir: &Path) -> Option<String> {
let lib_names = [

Check warning on line 47 in crates/prover-client/build.rs

View workflow job for this annotation

GitHub Actions / pre-commit-arm64

Diff in /home/runner/work/monbijou/monbijou/crates/prover-client/build.rs

Check warning on line 47 in crates/prover-client/build.rs

View workflow job for this annotation

GitHub Actions / pre-commit-x86_64

Diff in /home/runner/work/monbijou/monbijou/crates/prover-client/build.rs
"libbinius_prover.so", // Linux dynamic
"libbinius_prover.dylib", // macOS dynamic
"binius_prover.dll", // Windows dynamic
"libbinius_prover.a", // Static library
];

for name in &lib_names {
if dir.join(name).exists() {
return Some(name.to_string());
}
}
None
}

fn verify_library_exists(dir: &Path) -> bool {
if !dir.exists() {

Check warning on line 63 in crates/prover-client/build.rs

View workflow job for this annotation

GitHub Actions / pre-commit-arm64

Diff in /home/runner/work/monbijou/monbijou/crates/prover-client/build.rs

Check warning on line 63 in crates/prover-client/build.rs

View workflow job for this annotation

GitHub Actions / pre-commit-x86_64

Diff in /home/runner/work/monbijou/monbijou/crates/prover-client/build.rs
return false;
}

// Check for different library naming conventions
let lib_names = [
"libbinius_prover.so", // Linux
"libbinius_prover.dylib", // macOS
"binius_prover.dll", // Windows
"libbinius_prover.a", // Static library
];

for name in &lib_names {
if dir.join(name).exists() {
return true;
}
}

false
}
45 changes: 45 additions & 0 deletions crates/prover-client/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use thiserror::Error;

/// Error types for the prover interface

Check warning on line 3 in crates/prover-client/src/error.rs

View workflow job for this annotation

GitHub Actions / pre-commit-arm64

Diff in /home/runner/work/monbijou/monbijou/crates/prover-client/src/error.rs

Check warning on line 3 in crates/prover-client/src/error.rs

View workflow job for this annotation

GitHub Actions / pre-commit-x86_64

Diff in /home/runner/work/monbijou/monbijou/crates/prover-client/src/error.rs
#[derive(Debug, Error)]
pub enum ProverError {
/// Invalid input data
#[error("Invalid input: {0}")]
InvalidInput(String),

/// Serialization error
#[error("Serialization error: {0}")]
Serialization(#[from] binius_utils::serialization::SerializationError),

/// FFI error from the closed-source prover
#[error("FFI error (code {0})")]
FFIError(i32),

/// Library not available
#[error("Library not available: {0}")]
LibraryNotAvailable(String),

/// Prover operation failed
#[error("Prover failed: {0}")]
ProverFailed(String),

/// IO error
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
}

impl ProverError {
/// Create an error from an FFI error code

Check warning on line 32 in crates/prover-client/src/error.rs

View workflow job for this annotation

GitHub Actions / pre-commit-arm64

Diff in /home/runner/work/monbijou/monbijou/crates/prover-client/src/error.rs

Check warning on line 32 in crates/prover-client/src/error.rs

View workflow job for this annotation

GitHub Actions / pre-commit-x86_64

Diff in /home/runner/work/monbijou/monbijou/crates/prover-client/src/error.rs
pub fn from_ffi_code(code: i32) -> Self {
match code {
-1 => ProverError::ProverFailed("General prover failure".to_string()),
-2 => ProverError::InvalidInput("Invalid constraint system".to_string()),
-3 => ProverError::InvalidInput("Invalid witness data".to_string()),
-4 => ProverError::ProverFailed("Out of memory".to_string()),
_ => ProverError::FFIError(code),
}
}
}

/// Result type for prover operations
pub type Result<T> = std::result::Result<T, ProverError>;

Check warning on line 45 in crates/prover-client/src/error.rs

View workflow job for this annotation

GitHub Actions / pre-commit-arm64

Diff in /home/runner/work/monbijou/monbijou/crates/prover-client/src/error.rs

Check warning on line 45 in crates/prover-client/src/error.rs

View workflow job for this annotation

GitHub Actions / pre-commit-x86_64

Diff in /home/runner/work/monbijou/monbijou/crates/prover-client/src/error.rs
Loading
Loading