Skip to content

Commit 2be72b2

Browse files
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`
1 parent 2dee78c commit 2be72b2

File tree

9 files changed

+187
-87
lines changed

9 files changed

+187
-87
lines changed

.github/workflows/rust.yml

+36
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,39 @@ jobs:
2828
- name: Check clippy warnings
2929
run: cargo clippy --all-targets --all-features -- -D warnings
3030

31+
build_nightly_wasm:
32+
runs-on: ubuntu-latest
33+
steps:
34+
- uses: actions/checkout@v2
35+
36+
- name: Install
37+
run: rustup default nightly
38+
39+
- name: Build without std
40+
run: cargo build --no-default-features --verbose
41+
42+
- name: Run tests without std
43+
run: cargo test --no-default-features --verbose
44+
45+
- name: Build examples without std
46+
run: cargo build --examples --no-default-features --verbose
47+
48+
- name: Install wasm32-wasi target
49+
run: rustup target add wasm32-wasi
50+
51+
- name: Install wasm32-unknown-unknown target
52+
run: rustup target add wasm32-unknown-unknown
53+
54+
- name: Build for target wasm-wasi
55+
run: RUSTFLAGS="" cargo build --target=wasm32-wasi --no-default-features --verbose
56+
57+
- name: Patch Cargo.toml for wasm-bindgen
58+
run: |
59+
echo "[dependencies.getrandom]" >> Cargo.toml
60+
echo "version = \"0.1\"" >> Cargo.toml
61+
echo "default-features = false" >> Cargo.toml
62+
echo "features = [\"wasm-bindgen\"]" >> Cargo.toml
63+
64+
- name: Build for target wasm32-unknown-unknown
65+
run: RUSTFLAGS="" cargo build --target=wasm32-unknown-unknown --no-default-features --verbose
66+

Cargo.toml

+37-17
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,24 @@ license-file = "LICENSE"
1111
keywords = ["zkSNARKs", "cryptography", "proofs"]
1212

1313
[dependencies]
14-
curve25519-dalek = {version = "3.2.0", features = ["serde"]}
15-
merlin = "3.0.0"
16-
rand = "0.7.3"
17-
digest = "0.8.1"
18-
sha3 = "0.8.2"
19-
byteorder = "1.3.4"
14+
curve25519-dalek = { version = "3.2.0", features = [
15+
"serde",
16+
"u64_backend",
17+
"alloc",
18+
], default-features = false }
19+
merlin = { version = "3.0.0", default-features = false }
20+
rand = { version = "0.7.3", features = ["getrandom"], default-features = false }
21+
digest = { version = "0.8.1", default-features = false }
22+
sha3 = { version = "0.8.2", default-features = false }
23+
byteorder = { version = "1.3.4", default-features = false }
2024
rayon = { version = "1.3.0", optional = true }
21-
serde = { version = "1.0.106", features = ["derive"] }
22-
bincode = "1.2.1"
23-
subtle = { version = "2.4", default-features = false }
24-
rand_core = { version = "0.5", default-features = false }
25-
zeroize = { version = "1", default-features = false }
26-
itertools = "0.10.0"
27-
colored = "2.0.0"
28-
flate2 = "1.0.14"
29-
thiserror = "1.0"
25+
serde = { version = "1.0.106", features = ["derive"], default-features = false }
26+
bincode = { version = "1.3.3", default-features = false }
27+
subtle = { version = "2.4", features = ["i128"], default-features = false }
28+
zeroize = { version = "1.5", default-features = false }
29+
itertools = { version = "0.10.0", default-features = false }
30+
colored = { version = "2.0.0", default-features = false, optional = true }
31+
flate2 = { version = "1.0.14" }
3032

3133
[dev-dependencies]
3234
criterion = "0.3.1"
@@ -38,20 +40,38 @@ path = "src/lib.rs"
3840
[[bin]]
3941
name = "snark"
4042
path = "profiler/snark.rs"
43+
required-features = ["std"]
4144

4245
[[bin]]
4346
name = "nizk"
4447
path = "profiler/nizk.rs"
48+
required-features = ["std"]
4549

4650
[[bench]]
4751
name = "snark"
4852
harness = false
53+
required-features = ["std"]
4954

5055
[[bench]]
5156
name = "nizk"
5257
harness = false
58+
required-features = ["std"]
5359

5460
[features]
55-
default = ["curve25519-dalek/simd_backend"]
61+
default = ["std", "simd_backend"]
62+
std = [
63+
"curve25519-dalek/std",
64+
"digest/std",
65+
"merlin/std",
66+
"rand/std",
67+
"sha3/std",
68+
"byteorder/std",
69+
"serde/std",
70+
"subtle/std",
71+
"zeroize/std",
72+
"itertools/use_std",
73+
"flate2/rust_backend",
74+
]
75+
simd_backend = ["curve25519-dalek/simd_backend"]
5676
multicore = ["rayon"]
57-
profile = []
77+
profile = ["colored"]

README.md

+82-47
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,37 @@
11
# Spartan: High-speed zkSNARKs without trusted setup
22

33
![Rust](https://github.com/microsoft/Spartan/workflows/Rust/badge.svg)
4-
[![](https://img.shields.io/crates/v/spartan.svg)]((https://crates.io/crates/spartan))
4+
[![](https://img.shields.io/crates/v/spartan.svg)](<(https://crates.io/crates/spartan)>)
55

66
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.
77

88
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.
99

10-
Note that this library has *not* received a security review or audit.
10+
Note that this library has _not_ received a security review or audit.
1111

1212
## Highlights
13+
1314
We now highlight Spartan's distinctive features.
1415

15-
* **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.
16+
- **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.
1617

17-
* **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.
18+
- **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.
1819

19-
* **Sub-linear verification costs:** Spartan is the first transparent proof system with sub-linear verification costs for arbitrary NP statements (e.g., R1CS).
20+
- **Sub-linear verification costs:** Spartan is the first transparent proof system with sub-linear verification costs for arbitrary NP statements (e.g., R1CS).
2021

21-
* **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`.
22+
- **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`.
2223

23-
* **State-of-the-art performance:**
24-
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.
24+
- **State-of-the-art performance:**
25+
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.
2526

2627
### Implementation details
27-
`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.
28+
29+
`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.
2830

2931
## Examples
32+
3033
To import `libspartan` into your Rust project, add the following dependency to `Cargo.toml`:
34+
3135
```text
3236
spartan = "0.7.1"
3337
```
@@ -36,11 +40,11 @@ The following example shows how to use `libspartan` to create and verify a SNARK
3640
Some of our public APIs' style is inspired by the underlying crates we use.
3741

3842
```rust
39-
# extern crate libspartan;
40-
# extern crate merlin;
41-
# use libspartan::{Instance, SNARKGens, SNARK};
42-
# use merlin::Transcript;
43-
# fn main() {
43+
extern crate libspartan;
44+
extern crate merlin;
45+
use libspartan::{Instance, SNARKGens, SNARK};
46+
use merlin::Transcript;
47+
fn main() {
4448
// specify the size of an R1CS instance
4549
let num_vars = 1024;
4650
let num_cons = 1024;
@@ -66,16 +70,17 @@ Some of our public APIs' style is inspired by the underlying crates we use.
6670
.verify(&comm, &inputs, &mut verifier_transcript, &gens)
6771
.is_ok());
6872
println!("proof verification successful!");
69-
# }
73+
}
7074
```
7175

7276
Here is another example to use the NIZK variant of the Spartan proof system:
77+
7378
```rust
74-
# extern crate libspartan;
75-
# extern crate merlin;
76-
# use libspartan::{Instance, NIZKGens, NIZK};
77-
# use merlin::Transcript;
78-
# fn main() {
79+
extern crate libspartan;
80+
extern crate merlin;
81+
use libspartan::{Instance, NIZKGens, NIZK};
82+
use merlin::Transcript;
83+
fn main() {
7984
// specify the size of an R1CS instance
8085
let num_vars = 1024;
8186
let num_cons = 1024;
@@ -97,20 +102,22 @@ Here is another example to use the NIZK variant of the Spartan proof system:
97102
.verify(&inst, &inputs, &mut verifier_transcript, &gens)
98103
.is_ok());
99104
println!("proof verification successful!");
100-
# }
105+
}
101106
```
102107

103108
Finally, we provide an example that specifies a custom R1CS instance instead of using a synthetic instance
109+
104110
```rust
105111
#![allow(non_snake_case)]
106-
# extern crate curve25519_dalek;
107-
# extern crate libspartan;
108-
# extern crate merlin;
109-
# use curve25519_dalek::scalar::Scalar;
110-
# use libspartan::{InputsAssignment, Instance, SNARKGens, VarsAssignment, SNARK};
111-
# use merlin::Transcript;
112-
# use rand::rngs::OsRng;
113-
# fn main() {
112+
extern crate curve25519_dalek;
113+
extern crate libspartan;
114+
extern crate merlin;
115+
use curve25519_dalek::scalar::Scalar;
116+
use libspartan::{InputsAssignment, Instance, SNARKGens, VarsAssignment, SNARK};
117+
use merlin::Transcript;
118+
use rand::rngs::OsRng;
119+
120+
fn main() {
114121
// produce a tiny instance
115122
let (
116123
num_cons,
@@ -146,17 +153,17 @@ Finally, we provide an example that specifies a custom R1CS instance instead of
146153
.verify(&comm, &assignment_inputs, &mut verifier_transcript, &gens)
147154
.is_ok());
148155
println!("proof verification successful!");
149-
# }
150-
151-
# fn produce_tiny_r1cs() -> (
152-
# usize,
153-
# usize,
154-
# usize,
155-
# usize,
156-
# Instance,
157-
# VarsAssignment,
158-
# InputsAssignment,
159-
# ) {
156+
}
157+
158+
fn produce_tiny_r1cs() -> (
159+
usize,
160+
usize,
161+
usize,
162+
usize,
163+
Instance,
164+
VarsAssignment,
165+
InputsAssignment,
166+
) {
160167
// We will use the following example, but one could construct any R1CS instance.
161168
// Our R1CS instance is three constraints over five variables and two public inputs
162169
// (Z0 + Z1) * I0 - Z2 = 0
@@ -182,7 +189,7 @@ Finally, we provide an example that specifies a custom R1CS instance instead of
182189
// a variable that holds a byte representation of 1
183190
let one = Scalar::one().to_bytes();
184191

185-
// R1CS is a set of three sparse matrices A B C, where is a row for every
192+
// R1CS is a set of three sparse matrices A B C, where is a row for every
186193
// constraint and a column for every entry in z = (vars, 1, inputs)
187194
// An R1CS instance is satisfiable iff:
188195
// 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
233240
inputs[0] = i0.to_bytes();
234241
inputs[1] = i1.to_bytes();
235242
let assignment_inputs = InputsAssignment::new(&inputs).unwrap();
236-
243+
237244
// check if the instance we created is satisfiable
238245
let res = inst.is_sat(&assignment_vars, &assignment_inputs);
239246
assert_eq!(res.unwrap(), true);
@@ -247,64 +254,92 @@ Finally, we provide an example that specifies a custom R1CS instance instead of
247254
assignment_vars,
248255
assignment_inputs,
249256
)
250-
# }
257+
}
251258
```
252259

253260
For more examples, see [`examples/`](examples) directory in this repo.
254261

255262
## Building `libspartan`
263+
256264
Install [`rustup`](https://rustup.rs/)
257265

258266
Switch to nightly Rust using `rustup`:
267+
259268
```text
260269
rustup default nightly
261270
```
262271

263272
Clone the repository:
273+
264274
```text
265275
git clone https://github.com/Microsoft/Spartan
266276
cd Spartan
267277
```
268278

269279
To build docs for public APIs of `libspartan`:
280+
270281
```text
271282
cargo doc
272283
```
273284

274285
To run tests:
286+
275287
```text
276288
RUSTFLAGS="-C target_cpu=native" cargo test
277289
```
278290

279291
To build `libspartan`:
292+
280293
```text
281294
RUSTFLAGS="-C target_cpu=native" cargo build --release
282295
```
283296

284297
> 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`.
285298
286299
### Supported features
287-
* `profile`: enables fine-grained profiling information (see below for its use)
300+
301+
- `std`: enables std features (enabled by default)
302+
- `simd_backend`: enables `curve25519-dalek`'s simd feature (enabled by default)
303+
- `profile`: enables fine-grained profiling information (see below for its use)
304+
305+
### WASM Support
306+
307+
`libspartan` depends upon `rand::OsRng` (internally uses `getrandom` crate), it has out of box support for `wasm32-wasi`.
308+
309+
For the target `wasm32-unknown-unknown` disable default features for spartan
310+
and add direct dependency on `getrandom` with `wasm-bindgen` feature enabled.
311+
312+
```toml
313+
[dependencies]
314+
spartan = { version = "0.7", default-features = false }
315+
# since spartan uses getrandom(rand's OsRng), we need to enable 'wasm-bindgen'
316+
# feature to make it feed rand seed from js/nodejs env
317+
# https://docs.rs/getrandom/0.1.16/getrandom/index.html#support-for-webassembly-and-asmjs
318+
getrandom = { version = "0.1", features = ["wasm-bindgen"] }
319+
```
288320

289321
## Performance
290322

291323
### End-to-end benchmarks
324+
292325
`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).
293326

294327
To run end-to-end benchmarks:
295-
```text
328+
329+
```text
296330
RUSTFLAGS="-C target_cpu=native" cargo bench
297331
```
298332

299333
### Fine-grained profiling
334+
300335
Build `libspartan` with `profile` feature enabled. It creates two profilers: `./target/release/snark` and `./target/release/nizk`.
301336

302337
These profilers report performance as depicted below (for varying R1CS instance sizes). The reported
303338
performance is from running the profilers on a Microsoft Surface Laptop 3 on a single CPU core of Intel Core i7-1065G7 running Ubuntu 20.04 (atop WSL2 on Windows 10).
304339
See Section 9 in our [paper](https://eprint.iacr.org/2019/550) to see how this compares with other zkSNARKs in the literature.
305340

306341
```text
307-
$ ./target/release/snark
342+
$ ./target/release/snark
308343
Profiler:: SNARK
309344
* number_of_constraints 1048576
310345
* number_of_variables 1048576
@@ -355,7 +390,7 @@ Profiler:: SNARK
355390
```
356391

357392
```text
358-
$ ./target/release/nizk
393+
$ ./target/release/nizk
359394
Profiler:: NIZK
360395
* number_of_constraints 1048576
361396
* number_of_variables 1048576

0 commit comments

Comments
 (0)