Skip to content

Commit 4320d4a

Browse files
committed
Add prover-interface
1 parent 29b2653 commit 4320d4a

File tree

12 files changed

+876
-9
lines changed

12 files changed

+876
-9
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[workspace]
2-
members = ["crates/*", "crates/frontend/ceck"]
2+
members = ["crates/*", "crates/frontend/ceck", "crates/prover-client"]
33
resolver = "2"
44

55
[workspace.package]

crates/prover-client/Cargo.toml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
[package]
2+
name = "binius-prover-client"
3+
version = "0.1.0"
4+
edition = "2021"
5+
authors = ["Irreducible Team <opensource@irreducible.com>"]
6+
description = "Simplified open-source interface to the Binius ZK prover using serialization"
7+
license = "MIT OR Apache-2.0"
8+
build = "build.rs"
9+
10+
[lib]
11+
# This is a Rust crate (rlib). The cdylib is only built for internal testing purposes.
12+
crate-type = ["rlib", "cdylib"]
13+
14+
[dependencies]
15+
# Core dependencies for serialization
16+
binius-core = { path = "../core" }
17+
binius-utils = { path = "../utils" }
18+
bytes = "1.7"
19+
20+
# Error handling
21+
thiserror = "2.0"
22+
anyhow = "1.0"
23+
24+
# Needed for the FFI implementation (only when building cdylib)
25+
binius-prover = { path = "../prover", optional = true }
26+
27+
[features]
28+
default = []
29+
# Enable building the FFI implementation (for cdylib)
30+
ffi-impl = ["binius-prover"]
31+
32+
[dev-dependencies]
33+
# For testing and benchmarking
34+
criterion = "0.6"

crates/prover-client/README.md

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# Binius Prover Client
2+
3+
A Rust crate providing a clean API for Binius ZK proof generation.
4+
5+
## Overview
6+
7+
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.
8+
9+
## Architecture
10+
11+
```
12+
┌─────────────────────┐ ┌─────────────────────┐ ┌──────────────────────┐
13+
│ Your Rust App │────│ Prover Client │────│ Binius Prover │
14+
│ │ │ (This Crate) │ │ (C Library) │
15+
│ - ConstraintSystem │ │ - ProverClient │ │ - binius_prove() │
16+
│ - ValuesData │ │ - Serialization │ │ - Proof generation │
17+
│ - Proof │ │ - Error handling │ │ │
18+
└─────────────────────┘ └─────────────────────┘ └──────────────────────┘
19+
```
20+
21+
## Installation
22+
23+
Add this crate to your `Cargo.toml`:
24+
25+
```toml
26+
[dependencies]
27+
binius-prover-client = { path = "../prover-client" }
28+
```
29+
30+
## Requirements
31+
32+
This crate requires the Binius prover to be available as a C library:
33+
34+
```bash
35+
export BINIUS_PROVER_LIB_PATH=/path/to/prover/library
36+
```
37+
38+
Without this, the crate will compile but return runtime errors when attempting to generate proofs.
39+
40+
## Usage
41+
42+
```rust
43+
use binius_prover_client::ProverClient;
44+
use binius_core::constraint_system::{ConstraintSystem, ValuesData};
45+
46+
// Create a prover client instance
47+
let prover = ProverClient::new(1); // log_inv_rate = 1
48+
49+
// Generate proof from constraint system and witness data
50+
let proof = prover.prove(&constraint_system, &public_witness, &private_witness)?;
51+
```
52+
53+
The crate provides three API methods:
54+
- `prove()` - Takes Rust types, handles serialization internally
55+
- `prove_serialized()` - Takes pre-serialized bytes, returns deserialized `Proof`
56+
- `prove_serialized_raw()` - Takes and returns raw bytes for maximum efficiency
57+
58+
## FFI Interface Details
59+
60+
The FFI boundary uses a single C function with serialized inputs/outputs:
61+
62+
```c
63+
// Returns proof size on success, negative error code on failure
64+
int32_t binius_prove(
65+
const uint8_t* cs_bytes, // Serialized ConstraintSystem
66+
size_t cs_len,
67+
const uint8_t* pub_witness_bytes, // Serialized public ValuesData
68+
size_t pub_witness_len,
69+
const uint8_t* priv_witness_bytes,// Serialized private ValuesData
70+
size_t priv_witness_len,
71+
uint32_t log_inv_rate, // Proof generation parameter
72+
uint8_t* proof_out, // Output buffer for serialized Proof
73+
size_t proof_capacity // Size of output buffer
74+
);
75+
```
76+
77+
### Error Codes
78+
79+
- **Positive number**: Size of the proof written to `proof_out` (success)
80+
- **-1**: Null pointer error
81+
- **-2**: Invalid input data
82+
- **-3**: Proving error
83+
- **-4**: Serialization error
84+
- **-5**: Output buffer too small
85+
86+
## Testing and Development
87+
88+
### Running the Test Suite
89+
90+
The crate includes a focused test suite with automatic FFI library management:
91+
92+
```bash
93+
# Quick test - builds FFI library and runs all tests
94+
./test_prover_client.sh
95+
```
96+
97+
This script will:
98+
1. Build the FFI library with the current implementation
99+
2. Set up library paths automatically
100+
3. Run integration tests for all API variants
101+
4. Verify FFI boundary crossing works correctly
102+
103+
### Manual Testing
104+
105+
```bash
106+
# Build the FFI library
107+
cargo build --release --features ffi-impl
108+
109+
# Set library path and run tests
110+
export BINIUS_PROVER_LIB_PATH=$(pwd)/target/release
111+
cargo test
112+
```
113+
114+
### Test Coverage
115+
116+
The test suite focuses on interface correctness:
117+
- **API methods**: All three variants (`prove`, `prove_serialized`, `prove_serialized_raw`)
118+
- **FFI boundary**: Verifies data crosses the FFI boundary correctly
119+
- **Serialization**: Ensures proper serialization/deserialization
120+
- **Trait implementation**: Tests Default trait and accessor methods
121+
122+
## Implementation Notes
123+
124+
### Library Detection
125+
126+
The crate's build script automatically detects the external prover library:
127+
128+
- Checks `BINIUS_PROVER_LIB_PATH` environment variable
129+
- Sets up linking when library is found
130+
- Provides graceful fallback when library is unavailable
131+
132+
### FFI Implementation
133+
134+
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.
135+
136+
## Advanced Usage
137+
138+
### Error Handling
139+
140+
The interface provides detailed error information:
141+
142+
```rust
143+
use binius_prover_client::{ProverClient, ProverError};
144+
145+
match prover.prove(&cs, &pub_witness, &priv_witness) {
146+
Ok(proof) => println!("Proof generated: {} bytes", proof.data().len()),
147+
Err(ProverError::LibraryNotAvailable(msg)) => {
148+
eprintln!("FFI library not found: {}", msg);
149+
// Handle library not available case
150+
}
151+
Err(ProverError::FfiError(code)) => {
152+
eprintln!("FFI error code: {}", code);
153+
// Handle specific FFI error codes
154+
}
155+
Err(e) => eprintln!("Other error: {}", e),
156+
}
157+
```
158+
159+
### Performance Considerations
160+
161+
- **Pre-serialized data**: Use `prove_serialized_raw()` when you already have serialized inputs
162+
- **Library linking**: Dynamic linking adds minimal overhead compared to proof generation time
163+
- **Memory management**: The FFI boundary uses copying; consider this for very large constraint systems
164+
165+
### Integration with Existing Code
166+
167+
The interface is designed to integrate easily with existing Binius workflows:
168+
169+
```rust
170+
// Works with existing constraint system construction
171+
let cs = constraint_system_builder.build();
172+
let witness = witness_builder.build();
173+
174+
// Drop-in replacement for direct prover usage
175+
let prover = ProverClient::new(log_inv_rate);
176+
let proof = prover.prove(&cs.constraint_system, &witness.public, &witness.private)?;
177+
178+
// Use proof with existing verification code
179+
verify_proof(&proof, &public_inputs)?;
180+
```

crates/prover-client/build.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use std::env;
2+
use std::path::{Path, PathBuf};
3+
4+
fn main() {
5+
// Tell Cargo about our custom cfg flags
6+
println!("cargo::rustc-check-cfg=cfg(has_binius_prover)");
7+
println!("cargo::rustc-check-cfg=cfg(no_binius_prover)");
8+
9+
// Skip library detection when we're building the FFI implementation itself
10+
// This avoids circular dependency when building as cdylib
11+
if env::var("CARGO_FEATURE_FFI_IMPL").is_ok() {
12+
println!("cargo:rustc-cfg=no_binius_prover");
13+
return;
14+
}
15+
16+
// Try to find the closed-source library via environment variable
17+
if let Ok(path) = env::var("BINIUS_PROVER_LIB_PATH") {
18+
let path = PathBuf::from(path);
19+
20+
if verify_library_exists(&path) {
21+
// Set up linking
22+
println!("cargo:rustc-link-search={}", path.display());
23+
println!("cargo:rustc-link-lib=binius_prover");
24+
25+
// Store the library info for runtime access
26+
println!("cargo:rustc-env=LINKED_BINIUS_LIB_PATH={}", path.display());
27+
if let Some(file_name) = find_library_file(&path) {
28+
println!("cargo:rustc-env=LINKED_BINIUS_LIB_NAME={}", file_name);
29+
}
30+
31+
// Set a cfg flag to enable integration tests
32+
println!("cargo:rustc-cfg=has_binius_prover");
33+
} else {
34+
println!("cargo:rustc-cfg=no_binius_prover");
35+
}
36+
} else {
37+
// Library not found - this is OK for development
38+
println!("cargo:rustc-cfg=no_binius_prover");
39+
}
40+
41+
// Always rerun if these change
42+
println!("cargo:rerun-if-env-changed=BINIUS_PROVER_LIB_PATH");
43+
println!("cargo:rerun-if-changed=build.rs");
44+
}
45+
46+
fn find_library_file(dir: &Path) -> Option<String> {
47+
let lib_names = [
48+
"libbinius_prover.so", // Linux dynamic
49+
"libbinius_prover.dylib", // macOS dynamic
50+
"binius_prover.dll", // Windows dynamic
51+
"libbinius_prover.a", // Static library
52+
];
53+
54+
for name in &lib_names {
55+
if dir.join(name).exists() {
56+
return Some(name.to_string());
57+
}
58+
}
59+
None
60+
}
61+
62+
fn verify_library_exists(dir: &Path) -> bool {
63+
if !dir.exists() {
64+
return false;
65+
}
66+
67+
// Check for different library naming conventions
68+
let lib_names = [
69+
"libbinius_prover.so", // Linux
70+
"libbinius_prover.dylib", // macOS
71+
"binius_prover.dll", // Windows
72+
"libbinius_prover.a", // Static library
73+
];
74+
75+
for name in &lib_names {
76+
if dir.join(name).exists() {
77+
return true;
78+
}
79+
}
80+
81+
false
82+
}

crates/prover-client/src/error.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use thiserror::Error;
2+
3+
/// Error types for the prover interface
4+
#[derive(Debug, Error)]
5+
pub enum ProverError {
6+
/// Invalid input data
7+
#[error("Invalid input: {0}")]
8+
InvalidInput(String),
9+
10+
/// Serialization error
11+
#[error("Serialization error: {0}")]
12+
Serialization(#[from] binius_utils::serialization::SerializationError),
13+
14+
/// FFI error from the closed-source prover
15+
#[error("FFI error (code {0})")]
16+
FFIError(i32),
17+
18+
/// Library not available
19+
#[error("Library not available: {0}")]
20+
LibraryNotAvailable(String),
21+
22+
/// Prover operation failed
23+
#[error("Prover failed: {0}")]
24+
ProverFailed(String),
25+
26+
/// IO error
27+
#[error("IO error: {0}")]
28+
Io(#[from] std::io::Error),
29+
}
30+
31+
impl ProverError {
32+
/// Create an error from an FFI error code
33+
pub fn from_ffi_code(code: i32) -> Self {
34+
match code {
35+
-1 => ProverError::ProverFailed("General prover failure".to_string()),
36+
-2 => ProverError::InvalidInput("Invalid constraint system".to_string()),
37+
-3 => ProverError::InvalidInput("Invalid witness data".to_string()),
38+
-4 => ProverError::ProverFailed("Out of memory".to_string()),
39+
_ => ProverError::FFIError(code),
40+
}
41+
}
42+
}
43+
44+
/// Result type for prover operations
45+
pub type Result<T> = std::result::Result<T, ProverError>;

0 commit comments

Comments
 (0)