Improve Support for WASM (#60)
* feat: use `XofReader` instead of `io::Read` for no_std compatibility * feat: remove `thiserror` dependency thiserror is not no_std safe * feat: move deps std features behind new `std` feature - add new `std` feature - add `simd_backend` - move `colored` dep behind profile feature * feat: use old rand(v0.7) for cubic example. since curve25519-dalek (v3) uses old rand(v0.7) we need this. should upgrade curve25519-dalek to v4 once it out of pre release * feat: only build bench & profile if std in enabled * feat: remove rand_core as dependency * feat(ci): add job to test wasm build * fix: rollback rand to v7 and update debug test * fix(ci): Cargo.toml patching * feat: make clippy happy * feat: add wasm doc in readme * feat: readme formatting * feat: derive `Default` for `ProofVerifyError`
This commit is contained in:
Родитель
2dee78cfc0
Коммит
2be72b2af2
|
@ -28,3 +28,39 @@ jobs:
|
||||||
- name: Check clippy warnings
|
- name: Check clippy warnings
|
||||||
run: cargo clippy --all-targets --all-features -- -D warnings
|
run: cargo clippy --all-targets --all-features -- -D warnings
|
||||||
|
|
||||||
|
build_nightly_wasm:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install
|
||||||
|
run: rustup default nightly
|
||||||
|
|
||||||
|
- name: Build without std
|
||||||
|
run: cargo build --no-default-features --verbose
|
||||||
|
|
||||||
|
- name: Run tests without std
|
||||||
|
run: cargo test --no-default-features --verbose
|
||||||
|
|
||||||
|
- name: Build examples without std
|
||||||
|
run: cargo build --examples --no-default-features --verbose
|
||||||
|
|
||||||
|
- name: Install wasm32-wasi target
|
||||||
|
run: rustup target add wasm32-wasi
|
||||||
|
|
||||||
|
- name: Install wasm32-unknown-unknown target
|
||||||
|
run: rustup target add wasm32-unknown-unknown
|
||||||
|
|
||||||
|
- name: Build for target wasm-wasi
|
||||||
|
run: RUSTFLAGS="" cargo build --target=wasm32-wasi --no-default-features --verbose
|
||||||
|
|
||||||
|
- name: Patch Cargo.toml for wasm-bindgen
|
||||||
|
run: |
|
||||||
|
echo "[dependencies.getrandom]" >> Cargo.toml
|
||||||
|
echo "version = \"0.1\"" >> Cargo.toml
|
||||||
|
echo "default-features = false" >> Cargo.toml
|
||||||
|
echo "features = [\"wasm-bindgen\"]" >> Cargo.toml
|
||||||
|
|
||||||
|
- name: Build for target wasm32-unknown-unknown
|
||||||
|
run: RUSTFLAGS="" cargo build --target=wasm32-unknown-unknown --no-default-features --verbose
|
||||||
|
|
||||||
|
|
54
Cargo.toml
54
Cargo.toml
|
@ -11,22 +11,24 @@ license-file = "LICENSE"
|
||||||
keywords = ["zkSNARKs", "cryptography", "proofs"]
|
keywords = ["zkSNARKs", "cryptography", "proofs"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
curve25519-dalek = {version = "3.2.0", features = ["serde"]}
|
curve25519-dalek = { version = "3.2.0", features = [
|
||||||
merlin = "3.0.0"
|
"serde",
|
||||||
rand = "0.7.3"
|
"u64_backend",
|
||||||
digest = "0.8.1"
|
"alloc",
|
||||||
sha3 = "0.8.2"
|
], default-features = false }
|
||||||
byteorder = "1.3.4"
|
merlin = { version = "3.0.0", default-features = false }
|
||||||
|
rand = { version = "0.7.3", features = ["getrandom"], default-features = false }
|
||||||
|
digest = { version = "0.8.1", default-features = false }
|
||||||
|
sha3 = { version = "0.8.2", default-features = false }
|
||||||
|
byteorder = { version = "1.3.4", default-features = false }
|
||||||
rayon = { version = "1.3.0", optional = true }
|
rayon = { version = "1.3.0", optional = true }
|
||||||
serde = { version = "1.0.106", features = ["derive"] }
|
serde = { version = "1.0.106", features = ["derive"], default-features = false }
|
||||||
bincode = "1.2.1"
|
bincode = { version = "1.3.3", default-features = false }
|
||||||
subtle = { version = "2.4", default-features = false }
|
subtle = { version = "2.4", features = ["i128"], default-features = false }
|
||||||
rand_core = { version = "0.5", default-features = false }
|
zeroize = { version = "1.5", default-features = false }
|
||||||
zeroize = { version = "1", default-features = false }
|
itertools = { version = "0.10.0", default-features = false }
|
||||||
itertools = "0.10.0"
|
colored = { version = "2.0.0", default-features = false, optional = true }
|
||||||
colored = "2.0.0"
|
flate2 = { version = "1.0.14" }
|
||||||
flate2 = "1.0.14"
|
|
||||||
thiserror = "1.0"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3.1"
|
criterion = "0.3.1"
|
||||||
|
@ -38,20 +40,38 @@ path = "src/lib.rs"
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "snark"
|
name = "snark"
|
||||||
path = "profiler/snark.rs"
|
path = "profiler/snark.rs"
|
||||||
|
required-features = ["std"]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "nizk"
|
name = "nizk"
|
||||||
path = "profiler/nizk.rs"
|
path = "profiler/nizk.rs"
|
||||||
|
required-features = ["std"]
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "snark"
|
name = "snark"
|
||||||
harness = false
|
harness = false
|
||||||
|
required-features = ["std"]
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "nizk"
|
name = "nizk"
|
||||||
harness = false
|
harness = false
|
||||||
|
required-features = ["std"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["curve25519-dalek/simd_backend"]
|
default = ["std", "simd_backend"]
|
||||||
|
std = [
|
||||||
|
"curve25519-dalek/std",
|
||||||
|
"digest/std",
|
||||||
|
"merlin/std",
|
||||||
|
"rand/std",
|
||||||
|
"sha3/std",
|
||||||
|
"byteorder/std",
|
||||||
|
"serde/std",
|
||||||
|
"subtle/std",
|
||||||
|
"zeroize/std",
|
||||||
|
"itertools/use_std",
|
||||||
|
"flate2/rust_backend",
|
||||||
|
]
|
||||||
|
simd_backend = ["curve25519-dalek/simd_backend"]
|
||||||
multicore = ["rayon"]
|
multicore = ["rayon"]
|
||||||
profile = []
|
profile = ["colored"]
|
||||||
|
|
127
README.md
127
README.md
|
@ -1,33 +1,37 @@
|
||||||
# Spartan: High-speed zkSNARKs without trusted setup
|
# Spartan: High-speed zkSNARKs without trusted setup
|
||||||
|
|
||||||
![Rust](https://github.com/microsoft/Spartan/workflows/Rust/badge.svg)
|
![Rust](https://github.com/microsoft/Spartan/workflows/Rust/badge.svg)
|
||||||
[![](https://img.shields.io/crates/v/spartan.svg)]((https://crates.io/crates/spartan))
|
[![](https://img.shields.io/crates/v/spartan.svg)](<(https://crates.io/crates/spartan)>)
|
||||||
|
|
||||||
Spartan is a high-speed zero-knowledge proof system, a cryptographic primitive that enables a prover to prove a mathematical statement to a verifier without revealing anything besides the validity of the statement. This repository provides `libspartan,` a Rust library that implements a zero-knowledge succinct non-interactive argument of knowledge (zkSNARK), which is a type of zero-knowledge proof system with short proofs and fast verification times. The details of the Spartan proof system are described in our [paper](https://eprint.iacr.org/2019/550) published at [CRYPTO 2020](https://crypto.iacr.org/2020/). The security of the Spartan variant implemented in this library is based on the discrete logarithm problem in the random oracle model.
|
Spartan is a high-speed zero-knowledge proof system, a cryptographic primitive that enables a prover to prove a mathematical statement to a verifier without revealing anything besides the validity of the statement. This repository provides `libspartan,` a Rust library that implements a zero-knowledge succinct non-interactive argument of knowledge (zkSNARK), which is a type of zero-knowledge proof system with short proofs and fast verification times. The details of the Spartan proof system are described in our [paper](https://eprint.iacr.org/2019/550) published at [CRYPTO 2020](https://crypto.iacr.org/2020/). The security of the Spartan variant implemented in this library is based on the discrete logarithm problem in the random oracle model.
|
||||||
|
|
||||||
A simple example application is proving the knowledge of a secret s such that H(s) == d for a public d, where H is a cryptographic hash function (e.g., SHA-256, Keccak). A more complex application is a database-backed cloud service that produces proofs of correct state machine transitions for auditability. See this [paper](https://eprint.iacr.org/2020/758.pdf) for an overview and this [paper](https://eprint.iacr.org/2018/907.pdf) for details.
|
A simple example application is proving the knowledge of a secret s such that H(s) == d for a public d, where H is a cryptographic hash function (e.g., SHA-256, Keccak). A more complex application is a database-backed cloud service that produces proofs of correct state machine transitions for auditability. See this [paper](https://eprint.iacr.org/2020/758.pdf) for an overview and this [paper](https://eprint.iacr.org/2018/907.pdf) for details.
|
||||||
|
|
||||||
Note that this library has *not* received a security review or audit.
|
Note that this library has _not_ received a security review or audit.
|
||||||
|
|
||||||
## Highlights
|
## Highlights
|
||||||
|
|
||||||
We now highlight Spartan's distinctive features.
|
We now highlight Spartan's distinctive features.
|
||||||
|
|
||||||
* **No "toxic" waste:** Spartan is a *transparent* zkSNARK and does not require a trusted setup. So, it does not involve any trapdoors that must be kept secret or require a multi-party ceremony to produce public parameters.
|
- **No "toxic" waste:** Spartan is a _transparent_ zkSNARK and does not require a trusted setup. So, it does not involve any trapdoors that must be kept secret or require a multi-party ceremony to produce public parameters.
|
||||||
|
|
||||||
* **General-purpose:** Spartan produces proofs for arbitrary NP statements. `libspartan` supports NP statements expressed as rank-1 constraint satisfiability (R1CS) instances, a popular language for which there exists efficient transformations and compiler toolchains from high-level programs of interest.
|
- **General-purpose:** Spartan produces proofs for arbitrary NP statements. `libspartan` supports NP statements expressed as rank-1 constraint satisfiability (R1CS) instances, a popular language for which there exists efficient transformations and compiler toolchains from high-level programs of interest.
|
||||||
|
|
||||||
* **Sub-linear verification costs:** Spartan is the first transparent proof system with sub-linear verification costs for arbitrary NP statements (e.g., R1CS).
|
- **Sub-linear verification costs:** Spartan is the first transparent proof system with sub-linear verification costs for arbitrary NP statements (e.g., R1CS).
|
||||||
|
|
||||||
* **Standardized security:** Spartan's security relies on the hardness of computing discrete logarithms (a standard cryptographic assumption) in the random oracle model. `libspartan` uses `ristretto255`, a prime-order group abstraction atop `curve25519` (a high-speed elliptic curve). We use [`curve25519-dalek`](https://docs.rs/curve25519-dalek) for arithmetic over `ristretto255`.
|
- **Standardized security:** Spartan's security relies on the hardness of computing discrete logarithms (a standard cryptographic assumption) in the random oracle model. `libspartan` uses `ristretto255`, a prime-order group abstraction atop `curve25519` (a high-speed elliptic curve). We use [`curve25519-dalek`](https://docs.rs/curve25519-dalek) for arithmetic over `ristretto255`.
|
||||||
|
|
||||||
* **State-of-the-art performance:**
|
- **State-of-the-art performance:**
|
||||||
Among transparent SNARKs, Spartan offers the fastest prover with speedups of 36–152× depending on the baseline, produces proofs that are shorter by 1.2–416×, and incurs the lowest verification times with speedups of 3.6–1326×. The only exception is proof sizes under Bulletproofs, but Bulletproofs incurs slower verification both asymptotically and concretely. When compared to the state-of-the-art zkSNARK with trusted setup, Spartan’s prover is 2× faster for arbitrary R1CS instances and 16× faster for data-parallel workloads.
|
Among transparent SNARKs, Spartan offers the fastest prover with speedups of 36–152× depending on the baseline, produces proofs that are shorter by 1.2–416×, and incurs the lowest verification times with speedups of 3.6–1326×. The only exception is proof sizes under Bulletproofs, but Bulletproofs incurs slower verification both asymptotically and concretely. When compared to the state-of-the-art zkSNARK with trusted setup, Spartan’s prover is 2× faster for arbitrary R1CS instances and 16× faster for data-parallel workloads.
|
||||||
|
|
||||||
### Implementation details
|
### Implementation details
|
||||||
`libspartan` uses [`merlin`](https://docs.rs/merlin/) to automate the Fiat-Shamir transform. We also introduce a new type called `RandomTape` that extends a `Transcript` in `merlin` to allow the prover's internal methods to produce private randomness using its private transcript without having to create `OsRng` objects throughout the code. An object of type `RandomTape` is initialized with a new random seed from `OsRng` for each proof produced by the library.
|
|
||||||
|
`libspartan` uses [`merlin`](https://docs.rs/merlin/) to automate the Fiat-Shamir transform. We also introduce a new type called `RandomTape` that extends a `Transcript` in `merlin` to allow the prover's internal methods to produce private randomness using its private transcript without having to create `OsRng` objects throughout the code. An object of type `RandomTape` is initialized with a new random seed from `OsRng` for each proof produced by the library.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
To import `libspartan` into your Rust project, add the following dependency to `Cargo.toml`:
|
To import `libspartan` into your Rust project, add the following dependency to `Cargo.toml`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
spartan = "0.7.1"
|
spartan = "0.7.1"
|
||||||
```
|
```
|
||||||
|
@ -36,11 +40,11 @@ The following example shows how to use `libspartan` to create and verify a SNARK
|
||||||
Some of our public APIs' style is inspired by the underlying crates we use.
|
Some of our public APIs' style is inspired by the underlying crates we use.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# extern crate libspartan;
|
extern crate libspartan;
|
||||||
# extern crate merlin;
|
extern crate merlin;
|
||||||
# use libspartan::{Instance, SNARKGens, SNARK};
|
use libspartan::{Instance, SNARKGens, SNARK};
|
||||||
# use merlin::Transcript;
|
use merlin::Transcript;
|
||||||
# fn main() {
|
fn main() {
|
||||||
// specify the size of an R1CS instance
|
// specify the size of an R1CS instance
|
||||||
let num_vars = 1024;
|
let num_vars = 1024;
|
||||||
let num_cons = 1024;
|
let num_cons = 1024;
|
||||||
|
@ -66,16 +70,17 @@ Some of our public APIs' style is inspired by the underlying crates we use.
|
||||||
.verify(&comm, &inputs, &mut verifier_transcript, &gens)
|
.verify(&comm, &inputs, &mut verifier_transcript, &gens)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
println!("proof verification successful!");
|
println!("proof verification successful!");
|
||||||
# }
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Here is another example to use the NIZK variant of the Spartan proof system:
|
Here is another example to use the NIZK variant of the Spartan proof system:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# extern crate libspartan;
|
extern crate libspartan;
|
||||||
# extern crate merlin;
|
extern crate merlin;
|
||||||
# use libspartan::{Instance, NIZKGens, NIZK};
|
use libspartan::{Instance, NIZKGens, NIZK};
|
||||||
# use merlin::Transcript;
|
use merlin::Transcript;
|
||||||
# fn main() {
|
fn main() {
|
||||||
// specify the size of an R1CS instance
|
// specify the size of an R1CS instance
|
||||||
let num_vars = 1024;
|
let num_vars = 1024;
|
||||||
let num_cons = 1024;
|
let num_cons = 1024;
|
||||||
|
@ -97,20 +102,22 @@ Here is another example to use the NIZK variant of the Spartan proof system:
|
||||||
.verify(&inst, &inputs, &mut verifier_transcript, &gens)
|
.verify(&inst, &inputs, &mut verifier_transcript, &gens)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
println!("proof verification successful!");
|
println!("proof verification successful!");
|
||||||
# }
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, we provide an example that specifies a custom R1CS instance instead of using a synthetic instance
|
Finally, we provide an example that specifies a custom R1CS instance instead of using a synthetic instance
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
# extern crate curve25519_dalek;
|
extern crate curve25519_dalek;
|
||||||
# extern crate libspartan;
|
extern crate libspartan;
|
||||||
# extern crate merlin;
|
extern crate merlin;
|
||||||
# use curve25519_dalek::scalar::Scalar;
|
use curve25519_dalek::scalar::Scalar;
|
||||||
# use libspartan::{InputsAssignment, Instance, SNARKGens, VarsAssignment, SNARK};
|
use libspartan::{InputsAssignment, Instance, SNARKGens, VarsAssignment, SNARK};
|
||||||
# use merlin::Transcript;
|
use merlin::Transcript;
|
||||||
# use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
# fn main() {
|
|
||||||
|
fn main() {
|
||||||
// produce a tiny instance
|
// produce a tiny instance
|
||||||
let (
|
let (
|
||||||
num_cons,
|
num_cons,
|
||||||
|
@ -146,17 +153,17 @@ Finally, we provide an example that specifies a custom R1CS instance instead of
|
||||||
.verify(&comm, &assignment_inputs, &mut verifier_transcript, &gens)
|
.verify(&comm, &assignment_inputs, &mut verifier_transcript, &gens)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
println!("proof verification successful!");
|
println!("proof verification successful!");
|
||||||
# }
|
}
|
||||||
|
|
||||||
# fn produce_tiny_r1cs() -> (
|
fn produce_tiny_r1cs() -> (
|
||||||
# usize,
|
usize,
|
||||||
# usize,
|
usize,
|
||||||
# usize,
|
usize,
|
||||||
# usize,
|
usize,
|
||||||
# Instance,
|
Instance,
|
||||||
# VarsAssignment,
|
VarsAssignment,
|
||||||
# InputsAssignment,
|
InputsAssignment,
|
||||||
# ) {
|
) {
|
||||||
// We will use the following example, but one could construct any R1CS instance.
|
// We will use the following example, but one could construct any R1CS instance.
|
||||||
// Our R1CS instance is three constraints over five variables and two public inputs
|
// Our R1CS instance is three constraints over five variables and two public inputs
|
||||||
// (Z0 + Z1) * I0 - Z2 = 0
|
// (Z0 + Z1) * I0 - Z2 = 0
|
||||||
|
@ -182,7 +189,7 @@ Finally, we provide an example that specifies a custom R1CS instance instead of
|
||||||
// a variable that holds a byte representation of 1
|
// a variable that holds a byte representation of 1
|
||||||
let one = Scalar::one().to_bytes();
|
let one = Scalar::one().to_bytes();
|
||||||
|
|
||||||
// R1CS is a set of three sparse matrices A B C, where is a row for every
|
// R1CS is a set of three sparse matrices A B C, where is a row for every
|
||||||
// constraint and a column for every entry in z = (vars, 1, inputs)
|
// constraint and a column for every entry in z = (vars, 1, inputs)
|
||||||
// An R1CS instance is satisfiable iff:
|
// An R1CS instance is satisfiable iff:
|
||||||
// Az \circ Bz = Cz, where z = (vars, 1, inputs)
|
// Az \circ Bz = Cz, where z = (vars, 1, inputs)
|
||||||
|
@ -233,7 +240,7 @@ Finally, we provide an example that specifies a custom R1CS instance instead of
|
||||||
inputs[0] = i0.to_bytes();
|
inputs[0] = i0.to_bytes();
|
||||||
inputs[1] = i1.to_bytes();
|
inputs[1] = i1.to_bytes();
|
||||||
let assignment_inputs = InputsAssignment::new(&inputs).unwrap();
|
let assignment_inputs = InputsAssignment::new(&inputs).unwrap();
|
||||||
|
|
||||||
// check if the instance we created is satisfiable
|
// check if the instance we created is satisfiable
|
||||||
let res = inst.is_sat(&assignment_vars, &assignment_inputs);
|
let res = inst.is_sat(&assignment_vars, &assignment_inputs);
|
||||||
assert_eq!(res.unwrap(), true);
|
assert_eq!(res.unwrap(), true);
|
||||||
|
@ -247,36 +254,42 @@ Finally, we provide an example that specifies a custom R1CS instance instead of
|
||||||
assignment_vars,
|
assignment_vars,
|
||||||
assignment_inputs,
|
assignment_inputs,
|
||||||
)
|
)
|
||||||
# }
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
For more examples, see [`examples/`](examples) directory in this repo.
|
For more examples, see [`examples/`](examples) directory in this repo.
|
||||||
|
|
||||||
## Building `libspartan`
|
## Building `libspartan`
|
||||||
|
|
||||||
Install [`rustup`](https://rustup.rs/)
|
Install [`rustup`](https://rustup.rs/)
|
||||||
|
|
||||||
Switch to nightly Rust using `rustup`:
|
Switch to nightly Rust using `rustup`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
rustup default nightly
|
rustup default nightly
|
||||||
```
|
```
|
||||||
|
|
||||||
Clone the repository:
|
Clone the repository:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
git clone https://github.com/Microsoft/Spartan
|
git clone https://github.com/Microsoft/Spartan
|
||||||
cd Spartan
|
cd Spartan
|
||||||
```
|
```
|
||||||
|
|
||||||
To build docs for public APIs of `libspartan`:
|
To build docs for public APIs of `libspartan`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
cargo doc
|
cargo doc
|
||||||
```
|
```
|
||||||
|
|
||||||
To run tests:
|
To run tests:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
RUSTFLAGS="-C target_cpu=native" cargo test
|
RUSTFLAGS="-C target_cpu=native" cargo test
|
||||||
```
|
```
|
||||||
|
|
||||||
To build `libspartan`:
|
To build `libspartan`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
RUSTFLAGS="-C target_cpu=native" cargo build --release
|
RUSTFLAGS="-C target_cpu=native" cargo build --release
|
||||||
```
|
```
|
||||||
|
@ -284,19 +297,41 @@ RUSTFLAGS="-C target_cpu=native" cargo build --release
|
||||||
> NOTE: We enable SIMD instructions in `curve25519-dalek` by default, so if it fails to build remove the "simd_backend" feature argument in `Cargo.toml`.
|
> NOTE: We enable SIMD instructions in `curve25519-dalek` by default, so if it fails to build remove the "simd_backend" feature argument in `Cargo.toml`.
|
||||||
|
|
||||||
### Supported features
|
### Supported features
|
||||||
* `profile`: enables fine-grained profiling information (see below for its use)
|
|
||||||
|
- `std`: enables std features (enabled by default)
|
||||||
|
- `simd_backend`: enables `curve25519-dalek`'s simd feature (enabled by default)
|
||||||
|
- `profile`: enables fine-grained profiling information (see below for its use)
|
||||||
|
|
||||||
|
### WASM Support
|
||||||
|
|
||||||
|
`libspartan` depends upon `rand::OsRng` (internally uses `getrandom` crate), it has out of box support for `wasm32-wasi`.
|
||||||
|
|
||||||
|
For the target `wasm32-unknown-unknown` disable default features for spartan
|
||||||
|
and add direct dependency on `getrandom` with `wasm-bindgen` feature enabled.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
spartan = { version = "0.7", default-features = false }
|
||||||
|
# since spartan uses getrandom(rand's OsRng), we need to enable 'wasm-bindgen'
|
||||||
|
# feature to make it feed rand seed from js/nodejs env
|
||||||
|
# https://docs.rs/getrandom/0.1.16/getrandom/index.html#support-for-webassembly-and-asmjs
|
||||||
|
getrandom = { version = "0.1", features = ["wasm-bindgen"] }
|
||||||
|
```
|
||||||
|
|
||||||
## Performance
|
## Performance
|
||||||
|
|
||||||
### End-to-end benchmarks
|
### End-to-end benchmarks
|
||||||
|
|
||||||
`libspartan` includes two benches: `benches/nizk.rs` and `benches/snark.rs`. If you report the performance of Spartan in a research paper, we recommend using these benches for higher accuracy instead of fine-grained profiling (listed below).
|
`libspartan` includes two benches: `benches/nizk.rs` and `benches/snark.rs`. If you report the performance of Spartan in a research paper, we recommend using these benches for higher accuracy instead of fine-grained profiling (listed below).
|
||||||
|
|
||||||
To run end-to-end benchmarks:
|
To run end-to-end benchmarks:
|
||||||
```text
|
|
||||||
|
```text
|
||||||
RUSTFLAGS="-C target_cpu=native" cargo bench
|
RUSTFLAGS="-C target_cpu=native" cargo bench
|
||||||
```
|
```
|
||||||
|
|
||||||
### Fine-grained profiling
|
### Fine-grained profiling
|
||||||
|
|
||||||
Build `libspartan` with `profile` feature enabled. It creates two profilers: `./target/release/snark` and `./target/release/nizk`.
|
Build `libspartan` with `profile` feature enabled. It creates two profilers: `./target/release/snark` and `./target/release/nizk`.
|
||||||
|
|
||||||
These profilers report performance as depicted below (for varying R1CS instance sizes). The reported
|
These profilers report performance as depicted below (for varying R1CS instance sizes). The reported
|
||||||
|
@ -304,7 +339,7 @@ performance is from running the profilers on a Microsoft Surface Laptop 3 on a s
|
||||||
See Section 9 in our [paper](https://eprint.iacr.org/2019/550) to see how this compares with other zkSNARKs in the literature.
|
See Section 9 in our [paper](https://eprint.iacr.org/2019/550) to see how this compares with other zkSNARKs in the literature.
|
||||||
|
|
||||||
```text
|
```text
|
||||||
$ ./target/release/snark
|
$ ./target/release/snark
|
||||||
Profiler:: SNARK
|
Profiler:: SNARK
|
||||||
* number_of_constraints 1048576
|
* number_of_constraints 1048576
|
||||||
* number_of_variables 1048576
|
* number_of_variables 1048576
|
||||||
|
@ -355,7 +390,7 @@ Profiler:: SNARK
|
||||||
```
|
```
|
||||||
|
|
||||||
```text
|
```text
|
||||||
$ ./target/release/nizk
|
$ ./target/release/nizk
|
||||||
Profiler:: NIZK
|
Profiler:: NIZK
|
||||||
* number_of_constraints 1048576
|
* number_of_constraints 1048576
|
||||||
* number_of_variables 1048576
|
* number_of_variables 1048576
|
||||||
|
|
|
@ -27,7 +27,7 @@ fn nizk_prove_benchmark(c: &mut Criterion) {
|
||||||
|
|
||||||
let gens = NIZKGens::new(num_cons, num_vars, num_inputs);
|
let gens = NIZKGens::new(num_cons, num_vars, num_inputs);
|
||||||
|
|
||||||
let name = format!("NIZK_prove_{}", num_vars);
|
let name = format!("NIZK_prove_{num_vars}");
|
||||||
group.bench_function(&name, move |b| {
|
group.bench_function(&name, move |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let mut prover_transcript = Transcript::new(b"example");
|
let mut prover_transcript = Transcript::new(b"example");
|
||||||
|
@ -61,7 +61,7 @@ fn nizk_verify_benchmark(c: &mut Criterion) {
|
||||||
let mut prover_transcript = Transcript::new(b"example");
|
let mut prover_transcript = Transcript::new(b"example");
|
||||||
let proof = NIZK::prove(&inst, vars, &inputs, &gens, &mut prover_transcript);
|
let proof = NIZK::prove(&inst, vars, &inputs, &gens, &mut prover_transcript);
|
||||||
|
|
||||||
let name = format!("NIZK_verify_{}", num_cons);
|
let name = format!("NIZK_verify_{num_cons}");
|
||||||
group.bench_function(&name, move |b| {
|
group.bench_function(&name, move |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let mut verifier_transcript = Transcript::new(b"example");
|
let mut verifier_transcript = Transcript::new(b"example");
|
||||||
|
|
|
@ -22,7 +22,7 @@ fn snark_encode_benchmark(c: &mut Criterion) {
|
||||||
let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons);
|
let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons);
|
||||||
|
|
||||||
// produce a commitment to R1CS instance
|
// produce a commitment to R1CS instance
|
||||||
let name = format!("SNARK_encode_{}", num_cons);
|
let name = format!("SNARK_encode_{num_cons}");
|
||||||
group.bench_function(&name, move |b| {
|
group.bench_function(&name, move |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
SNARK::encode(black_box(&inst), black_box(&gens));
|
SNARK::encode(black_box(&inst), black_box(&gens));
|
||||||
|
@ -51,7 +51,7 @@ fn snark_prove_benchmark(c: &mut Criterion) {
|
||||||
let (comm, decomm) = SNARK::encode(&inst, &gens);
|
let (comm, decomm) = SNARK::encode(&inst, &gens);
|
||||||
|
|
||||||
// produce a proof
|
// produce a proof
|
||||||
let name = format!("SNARK_prove_{}", num_cons);
|
let name = format!("SNARK_prove_{num_cons}");
|
||||||
group.bench_function(&name, move |b| {
|
group.bench_function(&name, move |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let mut prover_transcript = Transcript::new(b"example");
|
let mut prover_transcript = Transcript::new(b"example");
|
||||||
|
@ -100,7 +100,7 @@ fn snark_verify_benchmark(c: &mut Criterion) {
|
||||||
);
|
);
|
||||||
|
|
||||||
// verify the proof
|
// verify the proof
|
||||||
let name = format!("SNARK_verify_{}", num_cons);
|
let name = format!("SNARK_verify_{num_cons}");
|
||||||
group.bench_function(&name, move |b| {
|
group.bench_function(&name, move |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let mut verifier_transcript = Transcript::new(b"example");
|
let mut verifier_transcript = Transcript::new(b"example");
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use super::group::{GroupElement, VartimeMultiscalarMul, GROUP_BASEPOINT_COMPRESSED};
|
use super::group::{GroupElement, VartimeMultiscalarMul, GROUP_BASEPOINT_COMPRESSED};
|
||||||
use super::scalar::Scalar;
|
use super::scalar::Scalar;
|
||||||
|
use digest::XofReader;
|
||||||
use digest::{ExtendableOutput, Input};
|
use digest::{ExtendableOutput, Input};
|
||||||
use sha3::Shake256;
|
use sha3::Shake256;
|
||||||
use std::io::Read;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MultiCommitGens {
|
pub struct MultiCommitGens {
|
||||||
|
@ -21,7 +21,7 @@ impl MultiCommitGens {
|
||||||
let mut gens: Vec<GroupElement> = Vec::new();
|
let mut gens: Vec<GroupElement> = Vec::new();
|
||||||
let mut uniform_bytes = [0u8; 64];
|
let mut uniform_bytes = [0u8; 64];
|
||||||
for _ in 0..n + 1 {
|
for _ in 0..n + 1 {
|
||||||
reader.read_exact(&mut uniform_bytes).unwrap();
|
reader.read(&mut uniform_bytes);
|
||||||
gens.push(GroupElement::from_uniform_bytes(&uniform_bytes));
|
gens.push(GroupElement::from_uniform_bytes(&uniform_bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,26 @@
|
||||||
use core::fmt::Debug;
|
use core::{
|
||||||
use thiserror::Error;
|
fmt::Display,
|
||||||
|
fmt::{self, Debug},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub enum ProofVerifyError {
|
pub enum ProofVerifyError {
|
||||||
#[error("Proof verification failed")]
|
#[default]
|
||||||
InternalError,
|
InternalError,
|
||||||
#[error("Compressed group element failed to decompress: {0:?}")]
|
|
||||||
DecompressionError([u8; 32]),
|
DecompressionError([u8; 32]),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ProofVerifyError {
|
impl Display for ProofVerifyError {
|
||||||
fn default() -> Self {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
ProofVerifyError::InternalError
|
match &self {
|
||||||
|
ProofVerifyError::DecompressionError(bytes) => write!(
|
||||||
|
f,
|
||||||
|
"Compressed group element failed to decompress: {bytes:?}",
|
||||||
|
),
|
||||||
|
ProofVerifyError::InternalError => {
|
||||||
|
write!(f, "Proof verification failed",)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,9 +90,9 @@ impl R1CSInstance {
|
||||||
B: &[(usize, usize, Scalar)],
|
B: &[(usize, usize, Scalar)],
|
||||||
C: &[(usize, usize, Scalar)],
|
C: &[(usize, usize, Scalar)],
|
||||||
) -> R1CSInstance {
|
) -> R1CSInstance {
|
||||||
Timer::print(&format!("number_of_constraints {}", num_cons));
|
Timer::print(&format!("number_of_constraints {num_cons}"));
|
||||||
Timer::print(&format!("number_of_variables {}", num_vars));
|
Timer::print(&format!("number_of_variables {num_vars}"));
|
||||||
Timer::print(&format!("number_of_inputs {}", num_inputs));
|
Timer::print(&format!("number_of_inputs {num_inputs}"));
|
||||||
Timer::print(&format!("number_non-zero_entries_A {}", A.len()));
|
Timer::print(&format!("number_non-zero_entries_A {}", A.len()));
|
||||||
Timer::print(&format!("number_non-zero_entries_B {}", B.len()));
|
Timer::print(&format!("number_non-zero_entries_B {}", B.len()));
|
||||||
Timer::print(&format!("number_non-zero_entries_C {}", C.len()));
|
Timer::print(&format!("number_non-zero_entries_C {}", C.len()));
|
||||||
|
@ -157,9 +157,9 @@ impl R1CSInstance {
|
||||||
num_vars: usize,
|
num_vars: usize,
|
||||||
num_inputs: usize,
|
num_inputs: usize,
|
||||||
) -> (R1CSInstance, Vec<Scalar>, Vec<Scalar>) {
|
) -> (R1CSInstance, Vec<Scalar>, Vec<Scalar>) {
|
||||||
Timer::print(&format!("number_of_constraints {}", num_cons));
|
Timer::print(&format!("number_of_constraints {num_cons}"));
|
||||||
Timer::print(&format!("number_of_variables {}", num_vars));
|
Timer::print(&format!("number_of_variables {num_vars}"));
|
||||||
Timer::print(&format!("number_of_inputs {}", num_inputs));
|
Timer::print(&format!("number_of_inputs {num_inputs}"));
|
||||||
|
|
||||||
let mut csprng: OsRng = OsRng;
|
let mut csprng: OsRng = OsRng;
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ use core::convert::TryFrom;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::iter::{Product, Sum};
|
use core::iter::{Product, Sum};
|
||||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||||
use rand_core::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
@ -813,7 +813,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{:?}", R2),
|
format!("{:?}", R2),
|
||||||
"0x1824b159acc5056f998c4fefecbc4ff55884b7fa0003480200000001fffffffe"
|
"0x0ffffffffffffffffffffffffffffffec6ef5bf4737dcf70d6ec31748d98951d"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче