You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

421 lines
15 KiB

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