| Crates.io | inspire |
| lib.rs | inspire |
| version | 0.1.2 |
| created_at | 2026-01-12 12:27:21.018251+00 |
| updated_at | 2026-01-12 12:27:21.018251+00 |
| description | InsPIRe: Communication-Efficient PIR with Server-side Preprocessing |
| homepage | https://github.com/igor53627/inspire-rs |
| repository | https://github.com/igor53627/inspire-rs |
| max_upload_size | |
| id | 2037646 |
| size | 1,589,784 |
InsPIRe: Communication-Efficient PIR with Server-side Preprocessing
A Rust implementation of the InsPIRe PIR (Private Information Retrieval) protocol, designed for private queries over large databases like Ethereum state (~73 GB, 2.4 billion entries).
InsPIRe achieves state-of-the-art communication efficiency for single-server PIR through:
cargo run --release --bin inspire-setup -- \
--data-dir path/to/plinko-output \
--output-dir ./inspire_data \
--ring-dim 2048 \
--binary-output # optional: also write binary shards for mmap
cargo run --release --bin inspire-server -- \
--data-dir ./inspire_data \
--bind 0.0.0.0:3000 \
--mmap # optional: use memory-mapped shards
# Query by raw index
cargo run --release --bin inspire-client -- \
--server http://localhost:3000 \
--secret-key inspire_data/secret_key.json \
Index --index 12345
# Query account by address
cargo run --release --bin inspire-client -- \
--server http://localhost:3000 \
--secret-key inspire_data/secret_key.json \
--account-mapping inspire_data/account-mapping.bin \
Account --address 0x...
The key insight: storing value y_k at coefficient k of polynomial h(X), then multiplying by X^{-k} (the inverse monomial) rotates y_k to coefficient 0.
| Parameter | Value | Notes |
|---|---|---|
| Ring dimension d | 2048 | Power of two |
| Ciphertext modulus q | CRT moduli [268369921, 249561089] (q ≈ 2^56) | NTT-friendly per-modulus |
| Plaintext modulus p | 2^16 | For 32-byte entries |
| Error σ | 6.4 | Discrete Gaussian |
| Key-switching matrices | 2 | K_g, K_h only (InspiRING) |
| Approach | KS Matrices | Conceptual Storage |
|---|---|---|
| Tree Packing | log(d) = 11 | 1056 KB |
| InspiRING (2-matrix) | 2 (seeds only) | 64 bytes |
| Reduction | 5.5x | 16,000x |
Note: The conceptual 64-byte figure refers only to InspiRING packing-key seeds. The actual ServerCrs in this implementation is ~40-50 MB (d=2048), dominated by crs_a_vectors (d×d coefficients ≈ 33 MB) and offline precomputation. The HTTP server defaults to InspiRING when clients send packing keys; tree packing is opt-in via packing_mode=tree. Default parameters now use CRT moduli (two residues per coefficient); single-modulus params remain for switched-query experiments.
cargo build --release
The library supports building for wasm32-unknown-unknown targets (browsers) by disabling server-specific features:
# In your Cargo.toml
[dependencies]
inspire = { version = "0.1", default-features = false }
Build with wasm-pack:
wasm-pack build --target web -- --no-default-features
Parallelism (rayon): The library uses rayon for parallel computation. On WASM:
-Z build-std + +atomics,+bulk-memory)wasm-bindgen-rayon for thread pool initializationRandom number generation: The rand crate requires getrandom JS support:
[dependencies]
getrandom = { version = "0.2", features = ["js"] }
Feature availability: With default-features = false:
cargo test
Generate test data files:
cargo run --example generate_test_data
This creates:
testdata/database.bin - 1024 entries of 32 bytestestdata/account-mapping.bin - 10 test accountstestdata/storage-mapping.bin - 20 test storage slotsRun PIR setup:
cargo run --release --bin inspire-setup -- \
--database testdata/database.bin \
--entry-size 32 \
--output-dir testdata/pir
InsPIRe offers 3 protocol variants with different bandwidth/computation tradeoffs:
| Variant | Query | Response | Total | Reduction |
|---|---|---|---|---|
| InsPIRe^0 (NoPacking) | 192 KB | 545 KB | 737 KB | baseline |
| InsPIRe^1 (OnePacking) | 192 KB | 32 KB | 224 KB | 3.3x |
| InsPIRe^2 (Seeded+Packed) | 96 KB | 32 KB | 128 KB | 5.7x |
Production recommendation: use InsPIRe^2 (TwoPacking) — seeded query + packed response.
These costs are independent of database size—the same whether querying 1 MB or 73 GB.
Note: The table reflects serialized sizes in single-modulus mode (crt_moduli length 1). With CRT enabled (default), ciphertexts store two residues per coefficient and sizes increase roughly proportionally.
Why constant sizes? This is a privacy requirement. If sizes varied with target index or database, traffic analysis could reveal what's being queried. See docs/COMMUNICATION_COSTS.md for the formulas.
use inspire::{query, query_seeded, respond_one_packing, respond_seeded_packed};
use inspire::params::InspireVariant;
// InsPIRe^0: Simple, no packing
let response = respond(&crs, &db, &query)?;
// InsPIRe^1: Packed response (17x response reduction)
let response = respond_one_packing(&crs, &db, &query)?;
// InsPIRe^2: Seeded query + packed response (5.7x total reduction)
let (state, seeded_query) = query_seeded(&crs, index, &config, &sk, &mut sampler)?;
let response = respond_seeded_packed(&crs, &db, &seeded_query)?;
// Extract with variant-specific extraction
let entry = extract_with_variant(&crs, &state, &response, entry_size, InspireVariant::TwoPacking)?;
Benchmarked on AMD/Intel x64 server with d=2048, 128-bit security:
| Database Size | Shards | Respond Time |
|---|---|---|
| 256K entries (8 MB) | 128 | 3.8 ms |
| 512K entries (16 MB) | 256 | 3.1 ms |
| 1M entries (32 MB) | 512 | 3.3 ms |
| Phase | Time |
|---|---|
| Client: Query generation (seeded) | ~4 ms |
| Server: Expand + Respond | ~3-4 ms |
| Client: Extract result | ~5 ms |
| Total round-trip | ~12 ms |
The implementation supports two packing approaches:
| Approach | Used By | Complexity | Notes |
|---|---|---|---|
| Tree packing | respond_one_packing() |
O(log d) key-switches | Opt-in via packing_mode=tree |
| InspiRING 2-matrix | respond_inspiring() |
O(γ × ℓ × n) online | Default for networked API (requires packing keys) |
Tree packing (via automorph_pack) is available as an explicit fallback. Set packing_mode=tree and use extract_with_variant(..., InspireVariant::OnePacking) to unpack. This uses log(d) Galois key-switching matrices stored in the CRS.
InspiRING 2-matrix packing is implemented in inspiring2 module with optimizations matching Google's reference:
HTTP API note: Query JSON includes packing_mode (\"inspiring\" or \"tree\", default \"inspiring\"). If packing_mode=\"inspiring\" and inspiring_packing_keys are missing, the server returns 400 with a descriptive error. Use packing_mode=\"tree\" to opt in to tree packing (both in-memory and mmap modes support InspiRING when keys are present).
Run benchmarks: cargo bench --bench packing
Run query size analysis: cargo run --release --example query_size_comparison
MIT OR Apache-2.0