arnaucube
arnaucube/testudoPublic Self-hosted repository
testudo/README.md
README.mdRaw
  1# Spartan: High-speed zkSNARKs without trusted setup
  2
  3![Rust](https://github.com/microsoft/Spartan/workflows/Rust/badge.svg)
  4[![](https://img.shields.io/crates/v/spartan.svg)]((https://crates.io/crates/curve25519-dalek))
  5
  6Spartan 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.
  7
  8A 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.
  9
 10Note that this library has *not* received a security review or audit.
 11
 12## Highlights
 13We now highlight Spartan's distinctive features.
 14
 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
 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
 19* **Sub-linear verification costs and linear-time proving costs:** Spartan is the first transparent proof system with sub-linear verification costs for arbitrary NP statements (e.g., R1CS). Spartan also features a linear-time prover, a property that has remained elusive for nearly all zkSNARKs in the literature.
 20
 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
 23* **State-of-the-art performance:** 
 24Among 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.
 25
 26### 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## Examples
 30To import `libspartan` into your Rust project, add the following dependency to `Cargo.toml`:
 31```text
 32spartan = "0.4.1"
 33```
 34
 35The following example shows how to use `libspartan` to create and verify a SNARK proof.
 36Some of our public APIs' style is inspired by the underlying crates we use.
 37
 38```rust
 39# extern crate libspartan;
 40# extern crate merlin;
 41# use libspartan::{Instance, SNARKGens, SNARK};
 42# use merlin::Transcript;
 43# fn main() {
 44    // specify the size of an R1CS instance
 45    let num_vars = 1024;
 46    let num_cons = 1024;
 47    let num_inputs = 10;
 48    let num_non_zero_entries = 1024;
 49
 50    // produce public parameters
 51    let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_non_zero_entries);
 52
 53    // ask the library to produce a synthentic R1CS instance
 54    let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs);
 55
 56    // create a commitment to the R1CS instance
 57    let (comm, decomm) = SNARK::encode(&inst, &gens);
 58
 59    // produce a proof of satisfiability
 60    let mut prover_transcript = Transcript::new(b"snark_example");
 61    let proof = SNARK::prove(&inst, &decomm, vars, &inputs, &gens, &mut prover_transcript);
 62
 63    // verify the proof of satisfiability
 64    let mut verifier_transcript = Transcript::new(b"snark_example");
 65    assert!(proof
 66      .verify(&comm, &inputs, &mut verifier_transcript, &gens)
 67      .is_ok());
 68    println!("proof verification successful!");
 69# }
 70```
 71
 72Here is another example to use the NIZK variant of the Spartan proof system:
 73```rust
 74# extern crate libspartan;
 75# extern crate merlin;
 76# use libspartan::{Instance, NIZKGens, NIZK};
 77# use merlin::Transcript;
 78# fn main() {
 79    // specify the size of an R1CS instance
 80    let num_vars = 1024;
 81    let num_cons = 1024;
 82    let num_inputs = 10;
 83
 84    // produce public parameters
 85    let gens = NIZKGens::new(num_cons, num_vars, num_inputs);
 86
 87    // ask the library to produce a synthentic R1CS instance
 88    let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs);
 89
 90    // produce a proof of satisfiability
 91    let mut prover_transcript = Transcript::new(b"nizk_example");
 92    let proof = NIZK::prove(&inst, vars, &inputs, &gens, &mut prover_transcript);
 93
 94    // verify the proof of satisfiability
 95    let mut verifier_transcript = Transcript::new(b"nizk_example");
 96    assert!(proof
 97      .verify(&inst, &inputs, &mut verifier_transcript, &gens)
 98      .is_ok());
 99    println!("proof verification successful!");
100# }
101```
102
103Finally, we provide an example that specifies a custom R1CS instance instead of using a synthetic instance
104```rust
105#![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() {
114  // produce a tiny instance
115  let (
116    num_cons,
117    num_vars,
118    num_inputs,
119    num_non_zero_entries,
120    inst,
121    assignment_vars,
122    assignment_inputs,
123  ) = produce_tiny_r1cs();
124
125  // produce public parameters
126  let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_non_zero_entries);
127
128  // create a commitment to the R1CS instance
129  let (comm, decomm) = SNARK::encode(&inst, &gens);
130
131  // produce a proof of satisfiability
132  let mut prover_transcript = Transcript::new(b"snark_example");
133  let proof = SNARK::prove(
134    &inst,
135    &decomm,
136    assignment_vars,
137    &assignment_inputs,
138    &gens,
139    &mut prover_transcript,
140  );
141
142  // verify the proof of satisfiability
143  let mut verifier_transcript = Transcript::new(b"snark_example");
144  assert!(proof
145    .verify(&comm, &assignment_inputs, &mut verifier_transcript, &gens)
146    .is_ok());
147  println!("proof verification successful!");
148# }
149
150# fn produce_tiny_r1cs() -> (
151#  usize,
152#  usize,
153#  usize,
154#  usize,
155#  Instance,
156#  VarsAssignment,
157#  InputsAssignment,
158# ) {
159  // We will use the following example, but one could construct any R1CS instance.
160  // Our R1CS instance is three constraints over five variables and two public inputs
161  // (Z0 + Z1) * I0 - Z2 = 0
162  // (Z0 + I1) * Z2 - Z3 = 0
163  // Z4 * 1 - 0 = 0
164
165  // parameters of the R1CS instance rounded to the nearest power of two
166  let num_cons = 4;
167  let num_vars = 5;
168  let num_inputs = 2;
169  let num_non_zero_entries = 5;
170
171  // We will encode the above constraints into three matrices, where
172  // the coefficients in the matrix are in the little-endian byte order
173  let mut A: Vec<(usize, usize, [u8; 32])> = Vec::new();
174  let mut B: Vec<(usize, usize, [u8; 32])> = Vec::new();
175  let mut C: Vec<(usize, usize, [u8; 32])> = Vec::new();
176
177  // The constraint system is defined over a finite field, which in our case is
178  // the scalar field of ristreeto255/curve25519 i.e., p =  2^{252}+27742317777372353535851937790883648493
179  // To construct these matrices, we will use `curve25519-dalek` but one can use any other method.
180
181  // a variable that holds a byte representation of 1
182  let one = Scalar::one().to_bytes();
183
184  // R1CS is a set of three sparse matrices A B C, where is a row for every 
185  // constraint and a column for every entry in z = (vars, 1, inputs)
186  // An R1CS instance is satisfiable iff:
187  // Az \circ Bz = Cz, where z = (vars, 1, inputs)
188
189  // constraint 0 entries in (A,B,C)
190  // constraint 0 is (Z0 + Z1) * I0 - Z2 = 0.
191  // We set 1 in matrix A for columns that correspond to Z0 and Z1
192  // We set 1 in matrix B for column that corresponds to I0
193  // We set 1 in matrix C for column that corresponds to Z2
194  A.push((0, 0, one));
195  A.push((0, 1, one));
196  B.push((0, num_vars + 1, one));
197  C.push((0, 2, one));
198
199  // constraint 1 entries in (A,B,C)
200  A.push((1, 0, one));
201  A.push((1, num_vars + 2, one));
202  B.push((1, 2, one));
203  C.push((1, 3, one));
204
205  // constraint 3 entries in (A,B,C)
206  A.push((2, 4, one));
207  B.push((2, num_vars, one));
208
209  let inst = Instance::new(num_cons, num_vars, num_inputs, &A, &B, &C).unwrap();
210
211  // compute a satisfying assignment
212  let mut csprng: OsRng = OsRng;
213  let i0 = Scalar::random(&mut csprng);
214  let i1 = Scalar::random(&mut csprng);
215  let z0 = Scalar::random(&mut csprng);
216  let z1 = Scalar::random(&mut csprng);
217  let z2 = (z0 + z1) * i0; // constraint 0
218  let z3 = (z0 + i1) * z2; // constraint 1
219  let z4 = Scalar::zero(); //constraint 2
220
221  // create a VarsAssignment
222  let mut vars = vec![Scalar::zero().to_bytes(); num_vars];
223  vars[0] = z0.to_bytes();
224  vars[1] = z1.to_bytes();
225  vars[2] = z2.to_bytes();
226  vars[3] = z3.to_bytes();
227  vars[4] = z4.to_bytes();
228  let assignment_vars = VarsAssignment::new(&vars).unwrap();
229
230  // create an InputsAssignment
231  let mut inputs = vec![Scalar::zero().to_bytes(); num_inputs];
232  inputs[0] = i0.to_bytes();
233  inputs[1] = i1.to_bytes();
234  let assignment_inputs = InputsAssignment::new(&inputs).unwrap();
235  
236  // check if the instance we created is satisfiable
237  let res = inst.is_sat(&assignment_vars, &assignment_inputs);
238  assert_eq!(res.unwrap(), true);
239
240  (
241    num_cons,
242    num_vars,
243    num_inputs,
244    num_non_zero_entries,
245    inst,
246    assignment_vars,
247    assignment_inputs,
248  )
249# }
250```
251
252For more examples, see [`examples/`](examples) directory in this repo.
253
254## Building `libspartan`
255Install [`rustup`](https://rustup.rs/)
256
257Switch to nightly Rust using `rustup`:
258```text
259rustup default nightly
260```
261
262Clone the repository:
263```text
264git clone https://github.com/Microsoft/Spartan
265cd Spartan
266```
267
268To build docs for public APIs of `libspartan`:
269```text
270cargo doc
271```
272
273To run tests:
274```text
275RUSTFLAGS="-C target_cpu=native" cargo test
276```
277
278To build `libspartan`:
279```text
280RUSTFLAGS="-C target_cpu=native" cargo build --release
281```
282
283> 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`.
284
285### Supported features
286* `profile`: enables fine-grained profiling information (see below for its use)
287
288## Performance
289
290### End-to-end benchmarks
291`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).
292
293To run end-to-end benchmarks:
294```text 
295RUSTFLAGS="-C target_cpu=native" cargo bench
296```
297
298### Fine-grained profiling
299Build `libspartan` with `profile` feature enabled. It creates two profilers: `./target/release/snark` and `./target/release/nizk`.
300
301These profilers report performance as depicted below (for varying R1CS instance sizes). The reported
302performance 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).
303See Section 9 in our [paper](https://eprint.iacr.org/2019/550) to see how this compares with other zkSNARKs in the literature.
304
305```text
306$ ./target/release/snark 
307Profiler:: SNARK
308  * number_of_constraints 1048576
309  * number_of_variables 1048576
310  * number_of_inputs 10
311  * number_non-zero_entries_A 1048576
312  * number_non-zero_entries_B 1048576
313  * number_non-zero_entries_C 1048576
314  * SNARK::encode
315  * SNARK::encode 14.2644201s
316  * SNARK::prove
317    * R1CSProof::prove
318      * polycommit
319      * polycommit 2.7175848s
320      * prove_sc_phase_one
321      * prove_sc_phase_one 683.7481ms
322      * prove_sc_phase_two
323      * prove_sc_phase_two 846.1056ms
324      * polyeval
325      * polyeval 193.4216ms
326    * R1CSProof::prove 4.4416193s
327    * len_r1cs_sat_proof 47024
328    * eval_sparse_polys
329    * eval_sparse_polys 377.357ms
330    * R1CSEvalProof::prove
331      * commit_nondet_witness
332      * commit_nondet_witness 14.4507331s
333      * build_layered_network
334      * build_layered_network 3.4360521s
335      * evalproof_layered_network
336        * len_product_layer_proof 64712
337      * evalproof_layered_network 15.5708066s
338    * R1CSEvalProof::prove 34.2930559s
339    * len_r1cs_eval_proof 133720
340  * SNARK::prove 39.1297568s
341  * SNARK::proof_compressed_len 141768
342  * SNARK::verify
343    * verify_sat_proof
344    * verify_sat_proof 20.0828ms
345    * verify_eval_proof
346      * verify_polyeval_proof
347        * verify_prod_proof
348        * verify_prod_proof 1.1847ms
349        * verify_hash_proof
350        * verify_hash_proof 81.06ms
351      * verify_polyeval_proof 82.3583ms
352    * verify_eval_proof 82.8937ms
353  * SNARK::verify 103.0536ms
354```
355
356```text
357$ ./target/release/nizk 
358Profiler:: NIZK
359  * number_of_constraints 1048576
360  * number_of_variables 1048576
361  * number_of_inputs 10
362  * number_non-zero_entries_A 1048576
363  * number_non-zero_entries_B 1048576
364  * number_non-zero_entries_C 1048576
365  * NIZK::prove
366    * R1CSProof::prove
367      * polycommit
368      * polycommit 2.7220635s
369      * prove_sc_phase_one
370      * prove_sc_phase_one 722.5487ms
371      * prove_sc_phase_two
372      * prove_sc_phase_two 862.6796ms
373      * polyeval
374      * polyeval 190.2233ms
375    * R1CSProof::prove 4.4982305s
376    * len_r1cs_sat_proof 47024
377  * NIZK::prove 4.5139888s
378  * NIZK::proof_compressed_len 48134
379  * NIZK::verify
380    * eval_sparse_polys
381    * eval_sparse_polys 395.0847ms
382    * verify_sat_proof
383    * verify_sat_proof 19.286ms
384  * NIZK::verify 414.5102ms
385```
386
387## LICENSE
388
389See [LICENSE](./LICENSE)
390
391## Contributing
392
393See [CONTRIBUTING](./CONTRIBUTING.md)