From c24a6abe99659b1e3446754841ab44bc6c8da408 Mon Sep 17 00:00:00 2001 From: Srinath Setty Date: Wed, 29 Apr 2020 10:31:41 -0700 Subject: [PATCH] Reduce the number of public APIs smaller and add more detailed documentation --- Cargo.toml | 9 +- NOTICE.md | 186 ++++++++------------ README.md | 242 ++++++++++++++++++++++++-- benches/nizk.rs | 19 +- benches/snark.rs | 43 ++--- profiler/nizk.rs | 27 ++- profiler/snark.rs | 32 ++-- src/dense_mlpoly.rs | 37 +--- src/lib.rs | 343 ++++++++++++++++++++++++++++++++++++- src/nizk/bullet.rs | 2 + src/r1csinstance.rs | 104 +++-------- src/r1csproof.rs | 18 +- src/scalar/ristretto255.rs | 5 +- src/sparse_mlpoly.rs | 144 ++++++++-------- src/spartan.rs | 282 ------------------------------ 15 files changed, 799 insertions(+), 694 deletions(-) delete mode 100644 src/spartan.rs diff --git a/Cargo.toml b/Cargo.toml index f3645b1..41a434e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Srinath Setty "] edition = "2018" [dependencies] -curve25519-dalek = { version = "2", features = ["serde"]} +curve25519-dalek = { version = "2", features = ["serde", "simd_backend"]} merlin = "2.0.0" rand = "0.7.3" digest = "0.8.1" @@ -45,8 +45,5 @@ name = "nizk" harness = false [features] -simd_backend = ["curve25519-dalek/simd_backend"] -rayon_par = [] -profile = [] - -default = ["simd_backend"] +multicore = [] +profile = [] \ No newline at end of file diff --git a/NOTICE.md b/NOTICE.md index 43b69c5..3eb6467 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1,121 +1,73 @@ This repository includes the following third-party open-source code. -* The code in src/scalar/ristretto255.rs is derived from [bls12-381](https://github.com/zkcrypto/bls12_381). +* The code in `src/scalar/ristretto255.rs` is derived from [bls12-381](https://github.com/zkcrypto/bls12_381). Specifically, from [src/bls12_381/scalar.rs](https://github.com/zkcrypto/bls12_381/blob/master/src/scalar.rs) and [src/bls12_381/util.rs](https://github.com/zkcrypto/bls12_381/blob/master/src/util.rs), which has the following copyright and license. -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. - - -* The invert and batch_invert methods in src/scalar/ristretto255.rs is from [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek), which has the following copyright and license. - -Copyright (c) 2016-2019 Isis Agora Lovecruft, Henry de Valence. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -======================================================================== - -Portions of curve25519-dalek were originally derived from Adam Langley's -Go ed25519 implementation, found at , -under the following licence: - -======================================================================== - -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -* The src/nizk/bullet.rs is derived from [bulletproofs](https://github.com/dalek-cryptography/bulletproofs/), which has the following license: - -MIT License - -Copyright (c) 2018 Chain, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file + Permission is hereby granted, free of charge, to any + person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the + Software without restriction, including without + limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software + is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice + shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF + ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + +* The `invert` and `batch_invert` methods in src/scalar/ristretto255.rs is from [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek), which has the following license. + + Copyright (c) 2016-2019 Isis Agora Lovecruft, Henry de Valence. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ======================================================================== + + Portions of curve25519-dalek were originally derived from Adam Langley's Go ed25519 implementation, found at , under the following licence: + + ======================================================================== + + Copyright (c) 2012 The Go Authors. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +* The module `src/nizk/bullet.rs` is derived from [bulletproofs](https://github.com/dalek-cryptography/bulletproofs/), which has the following license: + + MIT License + + Copyright (c) 2018 Chain, Inc. + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 34b4b26..badffb8 100644 --- a/README.md +++ b/README.md @@ -2,29 +2,237 @@ ![Rust](https://github.com/microsoft/Spartan/workflows/Rust/badge.svg) -Spartan is a research project to design high-speed zero-knowledge proof systems, a cryptographic protocol that enables a prover to prove a mathematical statement (e.g., that a given program was executed correctly) without revealing anything besides the validity of the statement. +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 current repository includes a library that implements -a zero-knowledge succinct non-interactive arguments of knowledge (zkSNARKs), a type of zero-knowledge proof system with short proofs and verification times. Unlike many other zkSNARKs, Spartan does not require a trusted setup and its security relies on the hardness of computing discrete logarithms (a well-studied assumption). The scheme is described in our [paper](https://eprint.iacr.org/2019/550). -## Building libspartan - cargo build - # On a machine that supports avx2 or ifma instructions: - export RUSTFLAGS="-C target_cpu=native" - cargo build --features "simd_backend" --release +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 stateful cloud service that produces proofs of correct state machine transitions for auditability. See this [paper](https://eprint.iacr.org/2020/758.pdf) for details. + +Note that this library has *not* received a security review or audit. + +## Highlights +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. + +* **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 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 time-optimal prover, a property that has remained elusive for nearly all zkSNARKs in the literature. + +* **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:** +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×. 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. + +### Status +Development is ongoing (PRs are welcome!). For example, the library does not yet offer APIs to specify an NP statement, but we will in the future offer standardized APIs and also integrate with a compiler that produces R1CS instances from high level programs. + +### 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. + +## Examples +The following example shows how to use `libspartan` to create and verify a SNARK proof. +Some of our public APIs' style is inspired by the underlying crates we use. + +```rust +# extern crate libspartan; +# extern crate merlin; +# use libspartan::{Instance, SNARKGens, SNARK}; +# use merlin::Transcript; +# fn main() { + // specify the size of an R1CS instance + let num_vars = 1024; + let num_cons = 1024; + let num_inputs = 10; + let num_non_zero_entries = 1024; + + // produce public parameters + let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_non_zero_entries); + + // ask the library to produce a synthentic R1CS instance + let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs); + + // create a commitment to the R1CS instance + let (comm, decomm) = SNARK::encode(&inst, &gens); + + // produce a proof of satisfiability + let mut prover_transcript = Transcript::new(b"snark_example"); + let proof = SNARK::prove(&inst, &decomm, vars, &inputs, &gens, &mut prover_transcript); + + // verify the proof of satisfiability + let mut verifier_transcript = Transcript::new(b"snark_example"); + assert!(proof + .verify(&comm, &inputs, &mut verifier_transcript, &gens) + .is_ok()); +# } +``` + +Here is another example to use the NIZK variant of the Spartan proof system: +```rust +# extern crate libspartan; +# extern crate merlin; +# use libspartan::{Instance, NIZKGens, NIZK}; +# use merlin::Transcript; +# fn main() { + // specify the size of an R1CS instance + let num_vars = 1024; + let num_cons = 1024; + let num_inputs = 10; + + // produce public parameters + let gens = NIZKGens::new(num_cons, num_vars); + + // ask the library to produce a synthentic R1CS instance + let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs); + + // produce a proof of satisfiability + let mut prover_transcript = Transcript::new(b"nizk_example"); + let proof = NIZK::prove(&inst, vars, &inputs, &gens, &mut prover_transcript); + + // verify the proof of satisfiability + let mut verifier_transcript = Transcript::new(b"nizk_example"); + assert!(proof + .verify(&inst, &inputs, &mut verifier_transcript, &gens) + .is_ok()); +# } +``` + +## Building `libspartan` +Install [`rustup`](https://rustup.rs/) + +Switch to nightly Rust using `rustup`: +```text +rustup default nightly +``` + +Clone the repository: +```text +git clone https://github.com/Microsoft/Spartan +cd Spartan +``` + +To build docs for public APIs of `libspartan`: +```text +cargo doc +``` + +To run tests: +```text +RUSTFLAGS="-C target_cpu=native" cargo test +``` + +To build `libspartan`: +```text +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`. + +### Supported features +* `profile`: enables fine-grained profiling information (see below for its use) ## Performance - cargo build - # On a machine that supports avx2 or ifma instructions: - export RUSTFLAGS="-C target_cpu=native" - cargo build --features "simd_backend,profile" --release - ./target/release/profiler - cargo bench - # On a machine that supports avx2 or ifma instructions: - export RUSTFLAGS="-C target_cpu=native" - cargo bench --features "simd_backend" +### 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). + +To run end-to-end benchmarks: +```text +RUSTFLAGS="-C target_cpu=native" cargo bench +``` + +### Fine-grained profiling +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 +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). +See Section 8 in our [paper](https://eprint.iacr.org/2019/550) to see how this compares with other zkSNARKs in the literature. + +```text +$ ./target/release/snark +Profiler:: SNARK + * number_of_constraints 1048576 + * number_of_variables 1048576 + * number_of_inputs 10 + * number_non-zero_entries_A 1048576 + * number_non-zero_entries_B 1048576 + * number_non-zero_entries_C 1048576 + * SNARK::encode + * SNARK::encode 14.2644201s + * SNARK::prove + * R1CSProof::prove + * polycommit + * polycommit 2.7175848s + * prove_sc_phase_one + * prove_sc_phase_one 683.7481ms + * prove_sc_phase_two + * prove_sc_phase_two 846.1056ms + * polyeval + * polyeval 193.4216ms + * R1CSProof::prove 4.4416193s + * len_r1cs_sat_proof 47024 + * eval_sparse_polys + * eval_sparse_polys 377.357ms + * R1CSEvalProof::prove + * commit_nondet_witness + * commit_nondet_witness 14.4507331s + * build_layered_network + * build_layered_network 3.4360521s + * evalproof_layered_network + * len_product_layer_proof 64712 + * evalproof_layered_network 15.5708066s + * R1CSEvalProof::prove 34.2930559s + * len_r1cs_eval_proof 133720 + * SNARK::prove 39.1297568s + * SNARK::proof_compressed_len 141768 + * SNARK::verify + * verify_sat_proof + * verify_sat_proof 20.0828ms + * verify_eval_proof + * verify_polyeval_proof + * verify_prod_proof + * verify_prod_proof 1.1847ms + * verify_hash_proof + * verify_hash_proof 81.06ms + * verify_polyeval_proof 82.3583ms + * verify_eval_proof 82.8937ms + * SNARK::verify 103.0536ms +``` +```text +$ ./target/release/nizk +Profiler:: NIZK + * number_of_constraints 1048576 + * number_of_variables 1048576 + * number_of_inputs 10 + * number_non-zero_entries_A 1048576 + * number_non-zero_entries_B 1048576 + * number_non-zero_entries_C 1048576 + * NIZK::prove + * R1CSProof::prove + * polycommit + * polycommit 2.7220635s + * prove_sc_phase_one + * prove_sc_phase_one 722.5487ms + * prove_sc_phase_two + * prove_sc_phase_two 862.6796ms + * polyeval + * polyeval 190.2233ms + * R1CSProof::prove 4.4982305s + * len_r1cs_sat_proof 47024 + * NIZK::prove 4.5139888s + * NIZK::proof_compressed_len 48134 + * NIZK::verify + * eval_sparse_polys + * eval_sparse_polys 395.0847ms + * verify_sat_proof + * verify_sat_proof 19.286ms + * NIZK::verify 414.5102ms +``` ## LICENSE diff --git a/benches/nizk.rs b/benches/nizk.rs index 10bd0ff..aec0740 100644 --- a/benches/nizk.rs +++ b/benches/nizk.rs @@ -7,8 +7,7 @@ extern crate merlin; extern crate rand; extern crate sha3; -use libspartan::r1csinstance::R1CSInstance; -use libspartan::spartan::{NIZKGens, NIZK}; +use libspartan::{Instance, NIZKGens, NIZK}; use merlin::Transcript; use criterion::*; @@ -23,19 +22,18 @@ fn nizk_prove_benchmark(c: &mut Criterion) { let num_cons = num_vars; let num_inputs = 10; - let (inst, vars, input) = R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - let n = inst.get_num_vars(); + let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs); let gens = NIZKGens::new(num_cons, num_vars); - let name = format!("NIZK_prove_{}", n); + let name = format!("NIZK_prove_{}", num_vars); group.bench_function(&name, move |b| { b.iter(|| { let mut prover_transcript = Transcript::new(b"example"); NIZK::prove( black_box(&inst), black_box(vars.clone()), - black_box(&input), + black_box(&inputs), black_box(&gens), black_box(&mut prover_transcript), ); @@ -54,23 +52,22 @@ fn nizk_verify_benchmark(c: &mut Criterion) { let num_vars = (2 as usize).pow(s as u32); let num_cons = num_vars; let num_inputs = 10; - let (inst, vars, input) = R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - let n = inst.get_num_vars(); + let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs); let gens = NIZKGens::new(num_cons, num_vars); // produce a proof of satisfiability let mut prover_transcript = Transcript::new(b"example"); - let proof = NIZK::prove(&inst, vars, &input, &gens, &mut prover_transcript); + let proof = NIZK::prove(&inst, vars, &inputs, &gens, &mut prover_transcript); - let name = format!("NIZK_verify_{}", n); + let name = format!("NIZK_verify_{}", num_cons); group.bench_function(&name, move |b| { b.iter(|| { let mut verifier_transcript = Transcript::new(b"example"); assert!(proof .verify( black_box(&inst), - black_box(&input), + black_box(&inputs), black_box(&mut verifier_transcript), black_box(&gens) ) diff --git a/benches/snark.rs b/benches/snark.rs index c631534..47a91de 100644 --- a/benches/snark.rs +++ b/benches/snark.rs @@ -1,14 +1,7 @@ -extern crate byteorder; -extern crate core; -extern crate criterion; -extern crate digest; extern crate libspartan; extern crate merlin; -extern crate rand; -extern crate sha3; -use libspartan::r1csinstance::R1CSInstance; -use libspartan::spartan::{SNARKGens, SNARK}; +use libspartan::{Instance, SNARKGens, SNARK}; use merlin::Transcript; use criterion::*; @@ -22,14 +15,13 @@ fn snark_encode_benchmark(c: &mut Criterion) { let num_vars = (2 as usize).pow(s as u32); let num_cons = num_vars; let num_inputs = 10; - let (inst, _vars, _input) = - R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - let n = inst.get_num_vars(); + let (inst, _vars, _inputs) = Instance::new(num_cons, num_vars, num_inputs); // produce public parameters - let gens = SNARKGens::new(&inst.size()); + let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); - let name = format!("SNARK_encode_{}", n); + // produce a commitment to R1CS instance + let name = format!("SNARK_encode_{}", num_cons); group.bench_function(&name, move |b| { b.iter(|| { SNARK::encode(black_box(&inst), black_box(&gens)); @@ -49,17 +41,16 @@ fn snark_prove_benchmark(c: &mut Criterion) { let num_cons = num_vars; let num_inputs = 10; - let (inst, vars, input) = R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - let n = inst.get_num_vars(); + let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs); // produce public parameters - let gens = SNARKGens::new(&inst.size()); + let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); - // encode the R1CS instance + // produce a commitment to R1CS instance let (_comm, decomm) = SNARK::encode(&inst, &gens); // produce a proof - let name = format!("SNARK_prove_{}", n); + let name = format!("SNARK_prove_{}", num_cons); group.bench_function(&name, move |b| { b.iter(|| { let mut prover_transcript = Transcript::new(b"example"); @@ -67,7 +58,7 @@ fn snark_prove_benchmark(c: &mut Criterion) { black_box(&inst), black_box(&decomm), black_box(vars.clone()), - black_box(&input), + black_box(&inputs), black_box(&gens), black_box(&mut prover_transcript), ); @@ -86,27 +77,27 @@ fn snark_verify_benchmark(c: &mut Criterion) { let num_vars = (2 as usize).pow(s as u32); let num_cons = num_vars; let num_inputs = 10; - let (inst, vars, input) = R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - let n = inst.get_num_vars(); + let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs); // produce public parameters - let gens = SNARKGens::new(&inst.size()); + let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); - // encode the R1CS instance + // produce a commitment to R1CS instance let (comm, decomm) = SNARK::encode(&inst, &gens); // produce a proof of satisfiability let mut prover_transcript = Transcript::new(b"example"); - let proof = SNARK::prove(&inst, &decomm, vars, &input, &gens, &mut prover_transcript); + let proof = SNARK::prove(&inst, &decomm, vars, &inputs, &gens, &mut prover_transcript); - let name = format!("SNARK_verify_{}", n); + // verify the proof + let name = format!("SNARK_verify_{}", num_cons); group.bench_function(&name, move |b| { b.iter(|| { let mut verifier_transcript = Transcript::new(b"example"); assert!(proof .verify( black_box(&comm), - black_box(&input), + black_box(&inputs), black_box(&mut verifier_transcript), black_box(&gens) ) diff --git a/profiler/nizk.rs b/profiler/nizk.rs index 2e80026..033327a 100644 --- a/profiler/nizk.rs +++ b/profiler/nizk.rs @@ -5,11 +5,14 @@ extern crate merlin; extern crate rand; use flate2::{write::ZlibEncoder, Compression}; -use libspartan::r1csinstance::R1CSInstance; -use libspartan::spartan::{NIZKGens, NIZK}; -use libspartan::timer::Timer; +use libspartan::{Instance, NIZKGens, NIZK}; use merlin::Transcript; +fn print(msg: &str) { + let star = "* "; + println!("{:indent$}{}{}", "", star, msg.to_string(), indent = 2); +} + pub fn main() { // the list of number of variables (and constraints) in an R1CS instance let inst_sizes = vec![10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; @@ -19,32 +22,26 @@ pub fn main() { let num_vars = (2 as usize).pow(s as u32); let num_cons = num_vars; let num_inputs = 10; - let (inst, vars, input) = R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - - Timer::print(&format!("number_of_constraints {}", num_cons)); + let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs); // produce public generators let gens = NIZKGens::new(num_cons, num_vars); // produce a proof of satisfiability - let timer_prove = Timer::new("NIZK::prove"); - let mut prover_transcript = Transcript::new(b"example"); - let proof = NIZK::prove(&inst, vars, &input, &gens, &mut prover_transcript); - timer_prove.stop(); + let mut prover_transcript = Transcript::new(b"nizk_example"); + let proof = NIZK::prove(&inst, vars, &inputs, &gens, &mut prover_transcript); let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default()); bincode::serialize_into(&mut encoder, &proof).unwrap(); let proof_encoded = encoder.finish().unwrap(); let msg_proof_len = format!("NIZK::proof_compressed_len {:?}", proof_encoded.len()); - Timer::print(&msg_proof_len); + print(&msg_proof_len); // verify the proof of satisfiability - let timer_verify = Timer::new("NIZK::verify"); - let mut verifier_transcript = Transcript::new(b"example"); + let mut verifier_transcript = Transcript::new(b"nizk_example"); assert!(proof - .verify(&inst, &input, &mut verifier_transcript, &gens) + .verify(&inst, &inputs, &mut verifier_transcript, &gens) .is_ok()); - timer_verify.stop(); println!(); } diff --git a/profiler/snark.rs b/profiler/snark.rs index de3d84f..165ade4 100644 --- a/profiler/snark.rs +++ b/profiler/snark.rs @@ -2,14 +2,16 @@ extern crate flate2; extern crate libspartan; extern crate merlin; -extern crate rand; use flate2::{write::ZlibEncoder, Compression}; -use libspartan::r1csinstance::R1CSInstance; -use libspartan::spartan::{SNARKGens, SNARK}; -use libspartan::timer::Timer; +use libspartan::{Instance, SNARKGens, SNARK}; use merlin::Transcript; +fn print(msg: &str) { + let star = "* "; + println!("{:indent$}{}{}", "", star, msg.to_string(), indent = 2); +} + pub fn main() { // the list of number of variables (and constraints) in an R1CS instance let inst_sizes = vec![10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; @@ -19,37 +21,29 @@ pub fn main() { let num_vars = (2 as usize).pow(s as u32); let num_cons = num_vars; let num_inputs = 10; - let (inst, vars, input) = R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - - Timer::print(&format!("number_of_constraints {}", num_cons)); + let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs); // produce public generators - let gens = SNARKGens::new(&inst.size()); + let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); // create a commitment to R1CSInstance - let timer_encode = Timer::new("SNARK::encode"); let (comm, decomm) = SNARK::encode(&inst, &gens); - timer_encode.stop(); // produce a proof of satisfiability - let timer_prove = Timer::new("SNARK::prove"); - let mut prover_transcript = Transcript::new(b"example"); - let proof = SNARK::prove(&inst, &decomm, vars, &input, &gens, &mut prover_transcript); - timer_prove.stop(); + let mut prover_transcript = Transcript::new(b"snark_example"); + let proof = SNARK::prove(&inst, &decomm, vars, &inputs, &gens, &mut prover_transcript); let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default()); bincode::serialize_into(&mut encoder, &proof).unwrap(); let proof_encoded = encoder.finish().unwrap(); let msg_proof_len = format!("SNARK::proof_compressed_len {:?}", proof_encoded.len()); - Timer::print(&msg_proof_len); + print(&msg_proof_len); // verify the proof of satisfiability - let timer_verify = Timer::new("SNARK::verify"); - let mut verifier_transcript = Transcript::new(b"example"); + let mut verifier_transcript = Transcript::new(b"snark_example"); assert!(proof - .verify(&comm, &input, &mut verifier_transcript, &gens) + .verify(&comm, &inputs, &mut verifier_transcript, &gens) .is_ok()); - timer_verify.stop(); println!(); } diff --git a/src/dense_mlpoly.rs b/src/dense_mlpoly.rs index fb4ad54..f12f6c6 100644 --- a/src/dense_mlpoly.rs +++ b/src/dense_mlpoly.rs @@ -11,7 +11,7 @@ use core::ops::Index; use merlin::Transcript; use serde::{Deserialize, Serialize}; -#[cfg(feature = "rayon_par")] +#[cfg(feature = "multicore")] use rayon::prelude::*; #[derive(Debug)] @@ -48,23 +48,6 @@ pub struct ConstPolyCommitment { C: CompressedGroup, } -impl PolyCommitment { - pub fn combine(&self, comm: &PolyCommitment, s: &Scalar) -> PolyCommitment { - assert_eq!(comm.C.len(), self.C.len()); - let C = (0..self.C.len()) - .map(|i| (self.C[i].decompress().unwrap() + s * comm.C[i].decompress().unwrap()).compress()) - .collect::>(); - PolyCommitment { C } - } - - pub fn combine_const(&self, comm: &ConstPolyCommitment) -> PolyCommitment { - let C = (0..self.C.len()) - .map(|i| (self.C[i].decompress().unwrap() + comm.C.decompress().unwrap()).compress()) - .collect::>(); - PolyCommitment { C } - } -} - pub struct EqPolynomial { r: Vec, } @@ -159,7 +142,7 @@ impl DensePolynomial { ) } - #[cfg(feature = "rayon_par")] + #[cfg(feature = "multicore")] fn commit_inner(&self, blinds: &Vec, gens: &MultiCommitGens) -> PolyCommitment { let L_size = blinds.len(); let R_size = self.Z.len() / L_size; @@ -176,7 +159,7 @@ impl DensePolynomial { PolyCommitment { C } } - #[cfg(not(feature = "rayon_par"))] + #[cfg(not(feature = "multicore"))] fn commit_inner(&self, blinds: &[Scalar], gens: &MultiCommitGens) -> PolyCommitment { let L_size = blinds.len(); let R_size = self.Z.len() / L_size; @@ -193,7 +176,6 @@ impl DensePolynomial { pub fn commit( &self, - hiding: bool, gens: &PolyCommitmentGens, random_tape: Option<&mut RandomTape>, ) -> (PolyCommitment, PolyCommitmentBlinds) { @@ -206,7 +188,7 @@ impl DensePolynomial { let R_size = right_num_vars.pow2(); assert_eq!(L_size * R_size, n); - let blinds = if hiding { + let blinds = if random_tape.is_some() { PolyCommitmentBlinds { blinds: random_tape.unwrap().random_vector(b"poly_blinds", L_size), } @@ -246,15 +228,6 @@ impl DensePolynomial { self.len = n; } - pub fn dotproduct(&self, other: &DensePolynomial) -> Scalar { - assert_eq!(self.len(), other.len()); - let mut res = Scalar::zero(); - for i in 0..self.len() { - res += self.Z[i] * other[i]; - } - res - } - // returns Z(r) in O(n) time pub fn evaluate(&self, r: &[Scalar]) -> Scalar { // r must have a value for each variable @@ -613,7 +586,7 @@ mod tests { assert_eq!(eval, (28 as usize).to_scalar()); let gens = PolyCommitmentGens::new(poly.get_num_vars(), b"test-two"); - let (poly_commitment, blinds) = poly.commit(false, &gens, None); + let (poly_commitment, blinds) = poly.commit(&gens, None); let mut random_tape = RandomTape::new(b"proof"); let mut prover_transcript = Transcript::new(b"example"); diff --git a/src/lib.rs b/src/lib.rs index 510d016..e36ea53 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,8 @@ #![allow(non_snake_case)] #![feature(test)] +#![deny(missing_docs)] +#![feature(external_doc)] +#![doc(include = "../README.md")] extern crate byteorder; extern crate core; @@ -18,13 +21,347 @@ mod group; mod math; mod nizk; mod product_tree; -pub mod r1csinstance; +mod r1csinstance; mod r1csproof; mod random; mod scalar; mod sparse_mlpoly; -pub mod spartan; mod sumcheck; -pub mod timer; +mod timer; mod transcript; mod unipoly; + +use errors::ProofVerifyError; +use merlin::Transcript; +use r1csinstance::{ + R1CSCommitment, R1CSCommitmentGens, R1CSDecommitment, R1CSEvalProof, R1CSInstance, +}; +use r1csproof::{R1CSGens, R1CSProof}; +use random::RandomTape; +use scalar::Scalar; +use serde::{Deserialize, Serialize}; +use timer::Timer; +use transcript::{AppendToTranscript, ProofTranscript}; + +/// `ComputationCommitment` holds a public preprocessed NP statement (e.g., R1CS) +pub struct ComputationCommitment { + comm: R1CSCommitment, +} + +/// `ComputationDecommitment` holds information to decommit `ComputationCommitment` +pub struct ComputationDecommitment { + decomm: R1CSDecommitment, +} + +/// `Instance` holds the description of R1CS matrices +pub struct Instance { + inst: R1CSInstance, +} + +impl Instance { + /// Constructs a new `Instance` and an associated satisfying assignment + pub fn new( + num_cons: usize, + num_vars: usize, + num_inputs: usize, + ) -> (Self, Vec, Vec) { + let (inst, vars, inputs) = R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); + (Instance { inst }, vars, inputs) + } +} + +/// `SNARKGens` holds public parameters for producing and verifying proofs with the Spartan SNARK +pub struct SNARKGens { + gens_r1cs_sat: R1CSGens, + gens_r1cs_eval: R1CSCommitmentGens, +} + +impl SNARKGens { + /// Constructs a new `SNARKGens` given the size of the R1CS statement + pub fn new(num_cons: usize, num_vars: usize, num_inputs: usize, num_nz_entries: usize) -> Self { + let gens_r1cs_sat = R1CSGens::new(b"gens_r1cs_sat", num_cons, num_vars); + let gens_r1cs_eval = R1CSCommitmentGens::new( + b"gens_r1cs_eval", + num_cons, + num_vars, + num_inputs, + num_nz_entries, + ); + SNARKGens { + gens_r1cs_sat, + gens_r1cs_eval, + } + } +} + +/// `SNARK` holds a proof produced by Spartan SNARK +#[derive(Serialize, Deserialize, Debug)] +pub struct SNARK { + r1cs_sat_proof: R1CSProof, + inst_evals: (Scalar, Scalar, Scalar), + r1cs_eval_proof: R1CSEvalProof, +} + +impl SNARK { + fn protocol_name() -> &'static [u8] { + b"Spartan SNARK proof" + } + + /// A public computation to create a commitment to an R1CS instance + pub fn encode( + inst: &Instance, + gens: &SNARKGens, + ) -> (ComputationCommitment, ComputationDecommitment) { + let timer_encode = Timer::new("SNARK::encode"); + let (comm, decomm) = inst.inst.commit(&gens.gens_r1cs_eval); + timer_encode.stop(); + ( + ComputationCommitment { comm }, + ComputationDecommitment { decomm }, + ) + } + + /// A method to produce a SNARK proof of the satisfiability of an R1CS instance + pub fn prove( + inst: &Instance, + decomm: &ComputationDecommitment, + vars: Vec, + input: &[Scalar], + gens: &SNARKGens, + transcript: &mut Transcript, + ) -> Self { + let timer_prove = Timer::new("SNARK::prove"); + + // we create a Transcript object seeded with a random Scalar + // to aid the prover produce its randomness + let mut random_tape = RandomTape::new(b"proof"); + transcript.append_protocol_name(SNARK::protocol_name()); + let (r1cs_sat_proof, rx, ry) = { + let (proof, rx, ry) = R1CSProof::prove( + &inst.inst, + vars, + input, + &gens.gens_r1cs_sat, + transcript, + &mut random_tape, + ); + let proof_encoded: Vec = bincode::serialize(&proof).unwrap(); + Timer::print(&format!("len_r1cs_sat_proof {:?}", proof_encoded.len())); + + (proof, rx, ry) + }; + + // We send evaluations of A, B, C at r = (rx, ry) as claims + // to enable the verifier complete the first sum-check + let timer_eval = Timer::new("eval_sparse_polys"); + let inst_evals = { + let (Ar, Br, Cr) = inst.inst.evaluate(&rx, &ry); + Ar.append_to_transcript(b"Ar_claim", transcript); + Br.append_to_transcript(b"Br_claim", transcript); + Cr.append_to_transcript(b"Cr_claim", transcript); + (Ar, Br, Cr) + }; + timer_eval.stop(); + + let r1cs_eval_proof = { + let proof = R1CSEvalProof::prove( + &decomm.decomm, + &rx, + &ry, + &inst_evals, + &gens.gens_r1cs_eval, + transcript, + &mut random_tape, + ); + + let proof_encoded: Vec = bincode::serialize(&proof).unwrap(); + Timer::print(&format!("len_r1cs_eval_proof {:?}", proof_encoded.len())); + proof + }; + + timer_prove.stop(); + SNARK { + r1cs_sat_proof, + inst_evals, + r1cs_eval_proof, + } + } + + /// A method to verify the SNARK proof of the satisfiability of an R1CS instance + pub fn verify( + &self, + comm: &ComputationCommitment, + input: &[Scalar], + transcript: &mut Transcript, + gens: &SNARKGens, + ) -> Result<(), ProofVerifyError> { + let timer_verify = Timer::new("SNARK::verify"); + transcript.append_protocol_name(SNARK::protocol_name()); + + let timer_sat_proof = Timer::new("verify_sat_proof"); + assert_eq!(input.len(), comm.comm.get_num_inputs()); + let (rx, ry) = self + .r1cs_sat_proof + .verify( + comm.comm.get_num_vars(), + comm.comm.get_num_cons(), + input, + &self.inst_evals, + transcript, + &gens.gens_r1cs_sat, + ) + .unwrap(); + timer_sat_proof.stop(); + + let timer_eval_proof = Timer::new("verify_eval_proof"); + let (Ar, Br, Cr) = &self.inst_evals; + Ar.append_to_transcript(b"Ar_claim", transcript); + Br.append_to_transcript(b"Br_claim", transcript); + Cr.append_to_transcript(b"Cr_claim", transcript); + assert!(self + .r1cs_eval_proof + .verify( + &comm.comm, + &rx, + &ry, + &self.inst_evals, + &gens.gens_r1cs_eval, + transcript + ) + .is_ok()); + timer_eval_proof.stop(); + timer_verify.stop(); + Ok(()) + } +} + +/// `NIZKGens` holds public parameters for producing and verifying proofs with the Spartan NIZK +pub struct NIZKGens { + gens_r1cs_sat: R1CSGens, +} + +impl NIZKGens { + /// Constructs a new `NIZKGens` given the size of the R1CS statement + pub fn new(num_cons: usize, num_vars: usize) -> Self { + let gens_r1cs_sat = R1CSGens::new(b"gens_r1cs_sat", num_cons, num_vars); + NIZKGens { gens_r1cs_sat } + } +} + +/// `NIZK` holds a proof produced by Spartan NIZK +#[derive(Serialize, Deserialize, Debug)] +pub struct NIZK { + r1cs_sat_proof: R1CSProof, + r: (Vec, Vec), +} + +impl NIZK { + fn protocol_name() -> &'static [u8] { + b"Spartan NIZK proof" + } + + /// A method to produce a NIZK proof of the satisfiability of an R1CS instance + pub fn prove( + inst: &Instance, + vars: Vec, + input: &[Scalar], + gens: &NIZKGens, + transcript: &mut Transcript, + ) -> Self { + let timer_prove = Timer::new("NIZK::prove"); + // we create a Transcript object seeded with a random Scalar + // to aid the prover produce its randomness + let mut random_tape = RandomTape::new(b"proof"); + transcript.append_protocol_name(NIZK::protocol_name()); + let (r1cs_sat_proof, rx, ry) = { + let (proof, rx, ry) = R1CSProof::prove( + &inst.inst, + vars, + input, + &gens.gens_r1cs_sat, + transcript, + &mut random_tape, + ); + let proof_encoded: Vec = bincode::serialize(&proof).unwrap(); + Timer::print(&format!("len_r1cs_sat_proof {:?}", proof_encoded.len())); + (proof, rx, ry) + }; + + timer_prove.stop(); + NIZK { + r1cs_sat_proof, + r: (rx, ry), + } + } + + /// A method to verify a NIZK proof of the satisfiability of an R1CS instance + pub fn verify( + &self, + inst: &Instance, + input: &[Scalar], + transcript: &mut Transcript, + gens: &NIZKGens, + ) -> Result<(), ProofVerifyError> { + let timer_verify = Timer::new("NIZK::verify"); + + transcript.append_protocol_name(NIZK::protocol_name()); + + // We send evaluations of A, B, C at r = (rx, ry) as claims + // to enable the verifier complete the first sum-check + let timer_eval = Timer::new("eval_sparse_polys"); + let (claimed_rx, claimed_ry) = &self.r; + let inst_evals = inst.inst.evaluate(claimed_rx, claimed_ry); + timer_eval.stop(); + + let timer_sat_proof = Timer::new("verify_sat_proof"); + assert_eq!(input.len(), inst.inst.get_num_inputs()); + let (rx, ry) = self + .r1cs_sat_proof + .verify( + inst.inst.get_num_vars(), + inst.inst.get_num_cons(), + input, + &inst_evals, + transcript, + &gens.gens_r1cs_sat, + ) + .unwrap(); + + // verify if claimed rx and ry are correct + assert_eq!(rx, *claimed_rx); + assert_eq!(ry, *claimed_ry); + timer_sat_proof.stop(); + timer_verify.stop(); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + pub fn check_snark() { + let num_vars = 256; + let num_cons = num_vars; + let num_inputs = 10; + let (inst, vars, inputs) = Instance::new(num_cons, num_vars, num_inputs); + + // produce public generators + let gens = SNARKGens::new(num_cons, num_vars, num_inputs, num_cons); + + // create a commitment to R1CSInstance + let (comm, decomm) = SNARK::encode(&inst, &gens); + + // produce a proof + let mut prover_transcript = Transcript::new(b"example"); + let proof = SNARK::prove(&inst, &decomm, vars, &inputs, &gens, &mut prover_transcript); + + // verify the proof + let mut verifier_transcript = Transcript::new(b"example"); + assert!(proof + .verify(&comm, &inputs, &mut verifier_transcript, &gens) + .is_ok()); + } +} diff --git a/src/nizk/bullet.rs b/src/nizk/bullet.rs index 0844b21..902e322 100644 --- a/src/nizk/bullet.rs +++ b/src/nizk/bullet.rs @@ -1,3 +1,5 @@ +//! This module is an adaptation of code from the bulletproofs crate. +//! See NOTICE.md for more details #![allow(non_snake_case)] #![allow(clippy::type_complexity)] #![allow(clippy::too_many_arguments)] diff --git a/src/r1csinstance.rs b/src/r1csinstance.rs index 697e08c..40b6f59 100644 --- a/src/r1csinstance.rs +++ b/src/r1csinstance.rs @@ -6,10 +6,8 @@ use super::scalar::Scalar; use super::sparse_mlpoly::{ MultiSparseMatPolynomialAsDense, SparseMatEntry, SparseMatPolyCommitment, SparseMatPolyCommitmentGens, SparseMatPolyEvalProof, SparseMatPolynomial, - SparseMatPolynomialSize, }; use super::timer::Timer; -use super::transcript::{AppendToTranscript, ProofTranscript}; use merlin::Transcript; use rand::rngs::OsRng; use serde::{Deserialize, Serialize}; @@ -24,38 +22,23 @@ pub struct R1CSInstance { C: SparseMatPolynomial, } -pub struct R1CSInstanceSize { - num_cons: usize, - num_vars: usize, - num_inputs: usize, - size_A: SparseMatPolynomialSize, - size_B: SparseMatPolynomialSize, - size_C: SparseMatPolynomialSize, -} - -impl R1CSInstanceSize { - pub fn get_num_cons(&self) -> usize { - self.num_cons - } - - pub fn get_num_vars(&self) -> usize { - self.num_vars - } - - pub fn get_num_inputs(&self) -> usize { - self.num_inputs - } -} - pub struct R1CSCommitmentGens { gens: SparseMatPolyCommitmentGens, } impl R1CSCommitmentGens { - pub fn new(size: &R1CSInstanceSize, label: &'static [u8]) -> R1CSCommitmentGens { - assert_eq!(size.size_A, size.size_B); - assert_eq!(size.size_A, size.size_C); - let gens = SparseMatPolyCommitmentGens::new(&size.size_A, 3, label); + pub fn new( + label: &'static [u8], + num_cons: usize, + num_vars: usize, + num_inputs: usize, + num_nz_entries: usize, + ) -> R1CSCommitmentGens { + assert!(num_inputs < num_vars); + let num_poly_vars_x = num_cons.log2(); + let num_poly_vars_y = (2 * num_vars).log2(); + let gens = + SparseMatPolyCommitmentGens::new(label, num_poly_vars_x, num_poly_vars_y, num_nz_entries, 3); R1CSCommitmentGens { gens } } } @@ -85,29 +68,6 @@ impl R1CSCommitment { } } -#[derive(Serialize, Deserialize, Debug)] -pub struct R1CSInstanceEvals { - eval_A_r: Scalar, - eval_B_r: Scalar, - eval_C_r: Scalar, -} - -impl R1CSInstanceEvals { - pub fn get_evaluations(&self) -> (Scalar, Scalar, Scalar) { - (self.eval_A_r, self.eval_B_r, self.eval_C_r) - } -} - -impl AppendToTranscript for R1CSInstanceEvals { - fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) { - transcript.append_message(label, b"R1CSInstanceEvals_begin"); - transcript.append_scalar(b"Ar_eval", &self.eval_A_r); - transcript.append_scalar(b"Br_eval", &self.eval_B_r); - transcript.append_scalar(b"Cr_eval", &self.eval_C_r); - transcript.append_message(label, b"R1CSInstanceEvals_end"); - } -} - impl R1CSInstance { pub fn new( num_cons: usize, @@ -139,22 +99,15 @@ impl R1CSInstance { self.num_inputs } - pub fn size(&self) -> R1CSInstanceSize { - R1CSInstanceSize { - num_cons: self.num_cons, - num_vars: self.num_vars, - num_inputs: self.num_inputs, - size_A: self.A.size(), - size_B: self.B.size(), - size_C: self.C.size(), - } - } - pub fn produce_synthetic_r1cs( num_cons: usize, num_vars: usize, num_inputs: usize, ) -> (R1CSInstance, Vec, Vec) { + Timer::print(&format!("number_of_constraints {}", num_cons)); + Timer::print(&format!("number_of_variables {}", num_vars)); + Timer::print(&format!("number_of_inputs {}", num_inputs)); + let mut csprng: OsRng = OsRng; // assert num_cons and num_vars are power of 2 @@ -202,6 +155,10 @@ impl R1CSInstance { } } + 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_C {}", C.len())); + let num_poly_vars_x = num_cons.log2(); let num_poly_vars_y = (2 * num_vars).log2(); let poly_A = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, A); @@ -282,16 +239,9 @@ impl R1CSInstance { (evals_A, evals_B, evals_C) } - pub fn evaluate_with_tables( - &self, - evals_rx: &[Scalar], - evals_ry: &[Scalar], - ) -> R1CSInstanceEvals { - R1CSInstanceEvals { - eval_A_r: self.A.evaluate_with_tables(evals_rx, evals_ry), - eval_B_r: self.B.evaluate_with_tables(evals_rx, evals_ry), - eval_C_r: self.C.evaluate_with_tables(evals_rx, evals_ry), - } + pub fn evaluate(&self, rx: &[Scalar], ry: &[Scalar]) -> (Scalar, Scalar, Scalar) { + let evals = SparseMatPolynomial::multi_evaluate(&[&self.A, &self.B, &self.C], rx, ry); + (evals[0], evals[1], evals[2]) } pub fn commit(&self, gens: &R1CSCommitmentGens) -> (R1CSCommitment, R1CSDecommitment) { @@ -321,7 +271,7 @@ impl R1CSEvalProof { decomm: &R1CSDecommitment, rx: &[Scalar], // point at which the polynomial is evaluated ry: &[Scalar], - evals: &R1CSInstanceEvals, + evals: &(Scalar, Scalar, Scalar), gens: &R1CSCommitmentGens, transcript: &mut Transcript, random_tape: &mut RandomTape, @@ -331,7 +281,7 @@ impl R1CSEvalProof { &decomm.dense, rx, ry, - &[evals.eval_A_r, evals.eval_B_r, evals.eval_C_r], + &[evals.0, evals.1, evals.2], &gens.gens, transcript, random_tape, @@ -346,7 +296,7 @@ impl R1CSEvalProof { comm: &R1CSCommitment, rx: &[Scalar], // point at which the R1CS matrix polynomials are evaluated ry: &[Scalar], - eval: &R1CSInstanceEvals, + evals: &(Scalar, Scalar, Scalar), gens: &R1CSCommitmentGens, transcript: &mut Transcript, ) -> Result<(), ProofVerifyError> { @@ -356,7 +306,7 @@ impl R1CSEvalProof { &comm.comm, rx, ry, - &[eval.eval_A_r, eval.eval_B_r, eval.eval_C_r], + &[evals.0, evals.1, evals.2], &gens.gens, transcript ) diff --git a/src/r1csproof.rs b/src/r1csproof.rs index a1d1cac..71c1c43 100644 --- a/src/r1csproof.rs +++ b/src/r1csproof.rs @@ -7,7 +7,7 @@ use super::errors::ProofVerifyError; use super::group::{CompressedGroup, GroupElement, VartimeMultiscalarMul}; use super::math::Math; use super::nizk::{EqualityProof, KnowledgeProof, ProductProof}; -use super::r1csinstance::{R1CSInstance, R1CSInstanceEvals}; +use super::r1csinstance::R1CSInstance; use super::random::RandomTape; use super::scalar::Scalar; use super::sparse_mlpoly::{SparsePolyEntry, SparsePolynomial}; @@ -66,7 +66,7 @@ pub struct R1CSGens { } impl R1CSGens { - pub fn new(_num_cons: usize, num_vars: usize, label: &'static [u8]) -> Self { + pub fn new(label: &'static [u8], _num_cons: usize, num_vars: usize) -> Self { let num_poly_vars = num_vars.log2(); let gens_pc = PolyCommitmentGens::new(num_poly_vars, label); let gens_sc = R1CSSumcheckGens::new(label, &gens_pc.gens.gens_1); @@ -162,7 +162,7 @@ impl R1CSProof { let poly_vars = DensePolynomial::new(vars.clone()); // produce a commitment to the satisfying assignment - let (comm_vars, blinds_vars) = poly_vars.commit(true, &gens.gens_pc, Some(random_tape)); + let (comm_vars, blinds_vars) = poly_vars.commit(&gens.gens_pc, Some(random_tape)); // add the commitment to the prover's transcript comm_vars.append_to_transcript(b"poly_commitment", transcript); @@ -352,7 +352,7 @@ impl R1CSProof { num_vars: usize, num_cons: usize, input: &[Scalar], - evals: &R1CSInstanceEvals, + evals: &(Scalar, Scalar, Scalar), transcript: &mut Transcript, gens: &R1CSGens, ) -> Result<(Vec, Vec), ProofVerifyError> { @@ -489,7 +489,7 @@ impl R1CSProof { ); // perform the final check in the second sum-check protocol - let (eval_A_r, eval_B_r, eval_C_r) = evals.get_evaluations(); + let (eval_A_r, eval_B_r, eval_C_r) = evals; let expected_claim_post_phase2 = ((r_A * eval_A_r + r_B * eval_B_r + r_C * eval_C_r) * comm_eval_Z_at_ry).compress(); // verify proof that expected_claim_post_phase1 == claim_post_phase1 @@ -597,7 +597,7 @@ mod tests { let num_inputs = 10; let (inst, vars, input) = R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - let gens = R1CSGens::new(num_cons, num_vars, b"test-m"); + let gens = R1CSGens::new(b"test-m", num_cons, num_vars); let mut random_tape = RandomTape::new(b"proof"); let mut prover_transcript = Transcript::new(b"example"); @@ -610,10 +610,8 @@ mod tests { &mut random_tape, ); - let eval_table_rx = EqPolynomial::new(rx).evals(); - let eval_table_ry = EqPolynomial::new(ry).evals(); - let inst_evals = inst.evaluate_with_tables(&eval_table_rx, &eval_table_ry); - + let inst_evals = inst.evaluate(&rx, &ry); + let mut verifier_transcript = Transcript::new(b"example"); assert!(proof .verify( diff --git a/src/scalar/ristretto255.rs b/src/scalar/ristretto255.rs index 1b6f75d..8a20b02 100755 --- a/src/scalar/ristretto255.rs +++ b/src/scalar/ristretto255.rs @@ -1,7 +1,8 @@ //! This module provides an implementation of the Curve25519's scalar field $\mathbb{F}_q$ //! where `q = 2^252 + 27742317777372353535851937790883648493 = 0x1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed` -//! The entire file is an adaptation from bls12-381 crate. We modify various constants (MODULUS, R, R2, etc.) to appropriate values for Curve25519 and update tests -//! We borrow the `invert` method from curve25519-dalek crate +//! This module is an adaptation of code from the bls12-381 crate. +//! We modify various constants (MODULUS, R, R2, etc.) to appropriate values for Curve25519 and update tests +//! We borrow the `invert` method from the curve25519-dalek crate. //! See NOTICE.md for more details #![allow(clippy::all)] use core::borrow::Borrow; diff --git a/src/sparse_mlpoly.rs b/src/sparse_mlpoly.rs index 6a6d78b..3c96f0a 100644 --- a/src/sparse_mlpoly.rs +++ b/src/sparse_mlpoly.rs @@ -51,18 +51,22 @@ impl Derefs { pub fn new(row_ops_val: Vec, col_ops_val: Vec) -> Self { assert_eq!(row_ops_val.len(), col_ops_val.len()); - // combine all polynomials into a single polynomial (used below to produce a single commitment) - let comb = DensePolynomial::merge(row_ops_val.iter().chain(col_ops_val.iter())); + let derefs = { + // combine all polynomials into a single polynomial (used below to produce a single commitment) + let comb = DensePolynomial::merge(row_ops_val.iter().chain(col_ops_val.iter())); + + Derefs { + row_ops_val, + col_ops_val, + comb, + } + }; - Derefs { - row_ops_val, - col_ops_val, - comb, - } + derefs } pub fn commit(&self, gens: &PolyCommitmentGens) -> DerefsCommitment { - let (comm_ops_val, _blinds) = self.comb.commit(false, gens, None); + let (comm_ops_val, _blinds) = self.comb.commit(gens, None); DerefsCommitment { comm_ops_val } } } @@ -91,25 +95,28 @@ impl DerefsEvalProof { evals.append_to_transcript(b"evals_ops_val", transcript); // n-to-1 reduction - let challenges = transcript.challenge_vector(b"challenge_combine_n_to_one", evals.len().log2()); - let mut poly_evals = DensePolynomial::new(evals); - for i in (0..challenges.len()).rev() { - poly_evals.bound_poly_var_bot(&challenges[i]); - } - assert_eq!(poly_evals.len(), 1); - let joint_claim_eval = poly_evals[0]; - let mut r_joint = challenges; - r_joint.extend(r); - - debug_assert_eq!(joint_poly.evaluate(&r_joint), joint_claim_eval); + let (r_joint, eval_joint) = { + let challenges = + transcript.challenge_vector(b"challenge_combine_n_to_one", evals.len().log2()); + let mut poly_evals = DensePolynomial::new(evals); + for i in (0..challenges.len()).rev() { + poly_evals.bound_poly_var_bot(&challenges[i]); + } + assert_eq!(poly_evals.len(), 1); + let joint_claim_eval = poly_evals[0]; + let mut r_joint = challenges; + r_joint.extend(r); + debug_assert_eq!(joint_poly.evaluate(&r_joint), joint_claim_eval); + (r_joint, joint_claim_eval) + }; // decommit the joint polynomial at r_joint - joint_claim_eval.append_to_transcript(b"joint_claim_eval", transcript); + eval_joint.append_to_transcript(b"joint_claim_eval", transcript); let (proof_derefs, _comm_derefs_eval) = PolyEvalProof::prove( joint_poly, None, &r_joint, - &joint_claim_eval, + &eval_joint, None, gens, transcript, @@ -131,10 +138,12 @@ impl DerefsEvalProof { ) -> Self { transcript.append_protocol_name(DerefsEvalProof::protocol_name()); - let mut evals = eval_row_ops_val_vec.to_owned(); - evals.extend(eval_col_ops_val_vec); - evals.resize(evals.len().next_power_of_two(), Scalar::zero()); - + let evals = { + let mut evals = eval_row_ops_val_vec.to_owned(); + evals.extend(eval_col_ops_val_vec); + evals.resize(evals.len().next_power_of_two(), Scalar::zero()); + evals + }; let proof_derefs = DerefsEvalProof::prove_single(&derefs.comb, r, evals, gens, transcript, random_tape); @@ -279,22 +288,6 @@ pub struct MultiSparseMatPolynomialAsDense { comb_mem: DensePolynomial, } -#[derive(Debug)] -pub struct SparseMatPolynomialSize { - size_ops: usize, - size_mem: usize, - size_derefs: usize, -} - -impl PartialEq for SparseMatPolynomialSize { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.size_derefs == other.size_derefs - && self.size_mem == other.size_mem - && self.size_ops == other.size_ops - } -} - pub struct SparseMatPolyCommitmentGens { gens_ops: PolyCommitmentGens, gens_mem: PolyCommitmentGens, @@ -303,13 +296,21 @@ pub struct SparseMatPolyCommitmentGens { impl SparseMatPolyCommitmentGens { pub fn new( - size: &SparseMatPolynomialSize, - batch_size: usize, label: &'static [u8], + num_vars_x: usize, + num_vars_y: usize, + num_nz_entries: usize, + batch_size: usize, ) -> SparseMatPolyCommitmentGens { - let num_vars_ops = size.size_ops + (batch_size * 5).next_power_of_two().log2(); - let num_vars_mem = size.size_mem + 1; - let num_vars_derefs = size.size_derefs + batch_size.next_power_of_two().log2(); + let num_vars_ops = + num_nz_entries.next_power_of_two().log2() + (batch_size * 5).next_power_of_two().log2(); + let num_vars_mem = if num_vars_x > num_vars_y { + num_vars_x + } else { + num_vars_y + } + 1; + let num_vars_derefs = + num_nz_entries.next_power_of_two().log2() + (batch_size * 2).next_power_of_two().log2(); let gens_ops = PolyCommitmentGens::new(num_vars_ops, label); let gens_mem = PolyCommitmentGens::new(num_vars_mem, label); @@ -416,19 +417,7 @@ impl SparseMatPolynomial { } } - pub fn size(&self) -> SparseMatPolynomialSize { - let dense = SparseMatPolynomial::multi_sparse_to_dense_rep(&[&self]); - - assert_eq!(dense.col.audit_ts.len(), dense.row.audit_ts.len()); - - SparseMatPolynomialSize { - size_mem: dense.row.audit_ts.get_num_vars(), - size_ops: dense.row.read_ts[0].get_num_vars(), - size_derefs: dense.row.read_ts[0].get_num_vars() + 1, - } - } - - pub fn evaluate_with_tables(&self, eval_table_rx: &[Scalar], eval_table_ry: &[Scalar]) -> Scalar { + fn evaluate_with_tables(&self, eval_table_rx: &[Scalar], eval_table_ry: &[Scalar]) -> Scalar { assert_eq!(self.num_vars_x.pow2(), eval_table_rx.len()); assert_eq!(self.num_vars_y.pow2(), eval_table_ry.len()); @@ -442,20 +431,17 @@ impl SparseMatPolynomial { .sum() } - pub fn evaluate(&self, rx: &[Scalar], ry: &[Scalar]) -> Scalar { + pub fn multi_evaluate( + polys: &[&SparseMatPolynomial], + rx: &[Scalar], + ry: &[Scalar], + ) -> Vec { let eval_table_rx = EqPolynomial::new(rx.to_vec()).evals(); let eval_table_ry = EqPolynomial::new(ry.to_vec()).evals(); - assert_eq!(self.num_vars_x.pow2(), eval_table_rx.len()); - assert_eq!(self.num_vars_y.pow2(), eval_table_ry.len()); - (0..self.M.len()) - .map(|i| { - let row = self.M[i].row; - let col = self.M[i].col; - let val = &self.M[i].val; - eval_table_rx[row] * eval_table_ry[col] * val - }) - .sum() + (0..polys.len()) + .map(|i| polys[i].evaluate_with_tables(&eval_table_rx, &eval_table_ry)) + .collect::>() } pub fn multiply_vec(&self, num_rows: usize, num_cols: usize, z: &[Scalar]) -> Vec { @@ -498,8 +484,8 @@ impl SparseMatPolynomial { let batch_size = sparse_polys.len(); let dense = SparseMatPolynomial::multi_sparse_to_dense_rep(sparse_polys); - let (comm_comb_ops, _blinds_comb_ops) = dense.comb_ops.commit(false, &gens.gens_ops, None); - let (comm_comb_mem, _blinds_comb_mem) = dense.comb_mem.commit(false, &gens.gens_mem, None); + let (comm_comb_ops, _blinds_comb_ops) = dense.comb_ops.commit(&gens.gens_ops, None); + let (comm_comb_mem, _blinds_comb_mem) = dense.comb_mem.commit(&gens.gens_mem, None); ( SparseMatPolyCommitment { @@ -885,7 +871,6 @@ impl HashLayerProof { // read for i in 0..eval_ops_addr.len() { - // TODO: should we verify length of these? let hash_read_at_rand_ops = hash_func(&eval_ops_addr[i], &eval_ops_val[i], &eval_read_ts[i]) - r_multiset_check; // verify the claim_last of init chunk assert_eq!(&hash_read_at_rand_ops, &claim_read[i]); @@ -1674,8 +1659,13 @@ mod tests { } let poly_M = SparseMatPolynomial::new(num_vars_x, num_vars_y, M); - let poly_M_size = poly_M.size(); - let gens = SparseMatPolyCommitmentGens::new(&poly_M_size, 3, b"gens_sparse_poly"); + let gens = SparseMatPolyCommitmentGens::new( + b"gens_sparse_poly", + num_vars_x, + num_vars_y, + num_nz_entries, + 3, + ); // commitment let (poly_comm, dense) = @@ -1688,8 +1678,8 @@ mod tests { let ry: Vec = (0..num_vars_y) .map(|_i| Scalar::random(&mut csprng)) .collect::>(); - let eval = poly_M.evaluate(&rx, &ry); - let evals = vec![eval, eval, eval]; + let eval = SparseMatPolynomial::multi_evaluate(&[&poly_M], &rx, &ry); + let evals = vec![eval[0], eval[0], eval[0]]; let mut random_tape = RandomTape::new(b"proof"); let mut prover_transcript = Transcript::new(b"example"); diff --git a/src/spartan.rs b/src/spartan.rs deleted file mode 100644 index 93c150a..0000000 --- a/src/spartan.rs +++ /dev/null @@ -1,282 +0,0 @@ -use super::dense_mlpoly::EqPolynomial; -use super::errors::ProofVerifyError; -use super::r1csinstance::{ - R1CSCommitment, R1CSCommitmentGens, R1CSDecommitment, R1CSEvalProof, R1CSInstance, - R1CSInstanceEvals, R1CSInstanceSize, -}; -use super::r1csproof::{R1CSGens, R1CSProof}; -use super::random::RandomTape; -use super::scalar::Scalar; -use super::timer::Timer; -use super::transcript::{AppendToTranscript, ProofTranscript}; -use merlin::Transcript; -use serde::{Deserialize, Serialize}; - -pub struct SNARKGens { - gens_r1cs_sat: R1CSGens, - gens_r1cs_eval: R1CSCommitmentGens, -} - -impl SNARKGens { - pub fn new(size: &R1CSInstanceSize) -> Self { - let gens_r1cs_sat = R1CSGens::new(size.get_num_cons(), size.get_num_vars(), b"gens_r1cs_sat"); - let gens_r1cs_eval = R1CSCommitmentGens::new(size, b"gens_r1cs_eval"); - SNARKGens { - gens_r1cs_sat, - gens_r1cs_eval, - } - } -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct SNARK { - r1cs_sat_proof: R1CSProof, - inst_evals: R1CSInstanceEvals, - r1cs_eval_proof: R1CSEvalProof, -} - -impl SNARK { - fn protocol_name() -> &'static [u8] { - b"Spartan SNARK proof" - } - - /// A public computation to create a commitment to an R1CS instance - pub fn encode(inst: &R1CSInstance, gens: &SNARKGens) -> (R1CSCommitment, R1CSDecommitment) { - inst.commit(&gens.gens_r1cs_eval) - } - - /// A method to produce a proof of the satisfiability of an R1CS instance - pub fn prove( - inst: &R1CSInstance, - decomm: &R1CSDecommitment, - vars: Vec, - input: &[Scalar], - gens: &SNARKGens, - transcript: &mut Transcript, - ) -> Self { - // we create a Transcript object seeded with a random Scalar - // to aid the prover produce its randomness - let mut random_tape = RandomTape::new(b"proof"); - transcript.append_protocol_name(SNARK::protocol_name()); - let (r1cs_sat_proof, rx, ry) = { - let (proof, rx, ry) = R1CSProof::prove( - inst, - vars, - input, - &gens.gens_r1cs_sat, - transcript, - &mut random_tape, - ); - let proof_encoded: Vec = bincode::serialize(&proof).unwrap(); - Timer::print(&format!("len_r1cs_sat_proof {:?}", proof_encoded.len())); - - (proof, rx, ry) - }; - - // We send evaluations of A, B, C at r = (rx, ry) as claims - // to enable the verifier complete the first sum-check - let timer_eval = Timer::new("eval_sparse_polys"); - let inst_evals = { - let eval_table_rx = EqPolynomial::new(rx.clone()).evals(); - let eval_table_ry = EqPolynomial::new(ry.clone()).evals(); - inst.evaluate_with_tables(&eval_table_rx, &eval_table_ry) - }; - inst_evals.append_to_transcript(b"r1cs_inst_evals", transcript); - timer_eval.stop(); - - let r1cs_eval_proof = { - let proof = R1CSEvalProof::prove( - decomm, - &rx, - &ry, - &inst_evals, - &gens.gens_r1cs_eval, - transcript, - &mut random_tape, - ); - - let proof_encoded: Vec = bincode::serialize(&proof).unwrap(); - Timer::print(&format!("len_r1cs_eval_proof {:?}", proof_encoded.len())); - proof - }; - - SNARK { - r1cs_sat_proof, - inst_evals, - r1cs_eval_proof, - } - } - - /// A method to verify the proof of the satisfiability of an R1CS instance - pub fn verify( - &self, - comm: &R1CSCommitment, - input: &[Scalar], - transcript: &mut Transcript, - gens: &SNARKGens, - ) -> Result<(), ProofVerifyError> { - transcript.append_protocol_name(SNARK::protocol_name()); - - let timer_sat_proof = Timer::new("verify_sat_proof"); - assert_eq!(input.len(), comm.get_num_inputs()); - let (rx, ry) = self - .r1cs_sat_proof - .verify( - comm.get_num_vars(), - comm.get_num_cons(), - input, - &self.inst_evals, - transcript, - &gens.gens_r1cs_sat, - ) - .unwrap(); - timer_sat_proof.stop(); - - let timer_eval_proof = Timer::new("verify_eval_proof"); - self - .inst_evals - .append_to_transcript(b"r1cs_inst_evals", transcript); - assert!(self - .r1cs_eval_proof - .verify( - comm, - &rx, - &ry, - &self.inst_evals, - &gens.gens_r1cs_eval, - transcript - ) - .is_ok()); - timer_eval_proof.stop(); - Ok(()) - } -} - -pub struct NIZKGens { - gens_r1cs_sat: R1CSGens, -} - -impl NIZKGens { - pub fn new(num_cons: usize, num_vars: usize) -> Self { - let gens_r1cs_sat = R1CSGens::new(num_cons, num_vars, b"gens_r1cs_sat"); - NIZKGens { gens_r1cs_sat } - } -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct NIZK { - r1cs_sat_proof: R1CSProof, - r: (Vec, Vec), -} - -impl NIZK { - fn protocol_name() -> &'static [u8] { - b"Spartan NIZK proof" - } - - /// A method to produce a proof of the satisfiability of an R1CS instance - pub fn prove( - inst: &R1CSInstance, - vars: Vec, - input: &[Scalar], - gens: &NIZKGens, - transcript: &mut Transcript, - ) -> Self { - // we create a Transcript object seeded with a random Scalar - // to aid the prover produce its randomness - let mut random_tape = RandomTape::new(b"proof"); - transcript.append_protocol_name(NIZK::protocol_name()); - let (r1cs_sat_proof, rx, ry) = { - let (proof, rx, ry) = R1CSProof::prove( - inst, - vars, - input, - &gens.gens_r1cs_sat, - transcript, - &mut random_tape, - ); - let proof_encoded: Vec = bincode::serialize(&proof).unwrap(); - Timer::print(&format!("len_r1cs_sat_proof {:?}", proof_encoded.len())); - - (proof, rx, ry) - }; - - NIZK { - r1cs_sat_proof, - r: (rx, ry), - } - } - - /// A method to verify the proof of the satisfiability of an R1CS instance - pub fn verify( - &self, - inst: &R1CSInstance, - input: &[Scalar], - transcript: &mut Transcript, - gens: &NIZKGens, - ) -> Result<(), ProofVerifyError> { - transcript.append_protocol_name(NIZK::protocol_name()); - - // We send evaluations of A, B, C at r = (rx, ry) as claims - // to enable the verifier complete the first sum-check - let timer_eval = Timer::new("eval_sparse_polys"); - let (claimed_rx, claimed_ry) = &self.r; - let inst_evals = { - let eval_table_rx = EqPolynomial::new(claimed_rx.clone()).evals(); - let eval_table_ry = EqPolynomial::new(claimed_ry.clone()).evals(); - inst.evaluate_with_tables(&eval_table_rx, &eval_table_ry) - }; - timer_eval.stop(); - - let timer_sat_proof = Timer::new("verify_sat_proof"); - assert_eq!(input.len(), inst.get_num_inputs()); - let (rx, ry) = self - .r1cs_sat_proof - .verify( - inst.get_num_vars(), - inst.get_num_cons(), - input, - &inst_evals, - transcript, - &gens.gens_r1cs_sat, - ) - .unwrap(); - - // verify if claimed rx and ry are correct - assert_eq!(rx, *claimed_rx); - assert_eq!(ry, *claimed_ry); - timer_sat_proof.stop(); - - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - pub fn check_snark() { - let num_vars = 256; - let num_cons = num_vars; - let num_inputs = 10; - let (inst, vars, input) = R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); - let r1cs_size = inst.size(); - - // produce public generators - let gens = SNARKGens::new(&r1cs_size); - - // create a commitment to R1CSInstance - let (comm, decomm) = SNARK::encode(&inst, &gens); - - // produce a proof - let mut prover_transcript = Transcript::new(b"example"); - let proof = SNARK::prove(&inst, &decomm, vars, &input, &gens, &mut prover_transcript); - - // verify the proof - let mut verifier_transcript = Transcript::new(b"example"); - assert!(proof - .verify(&comm, &input, &mut verifier_transcript, &gens) - .is_ok()); - } -}