Browse Source

Reduce the number of public APIs smaller and add more detailed documentation

master
Srinath Setty 4 years ago
parent
commit
c24a6abe99
15 changed files with 799 additions and 694 deletions
  1. +3
    -6
      Cargo.toml
  2. +69
    -117
      NOTICE.md
  3. +225
    -17
      README.md
  4. +8
    -11
      benches/nizk.rs
  5. +17
    -26
      benches/snark.rs
  6. +12
    -15
      profiler/nizk.rs
  7. +13
    -19
      profiler/snark.rs
  8. +5
    -32
      src/dense_mlpoly.rs
  9. +340
    -3
      src/lib.rs
  10. +2
    -0
      src/nizk/bullet.rs
  11. +27
    -77
      src/r1csinstance.rs
  12. +8
    -10
      src/r1csproof.rs
  13. +3
    -2
      src/scalar/ristretto255.rs
  14. +67
    -77
      src/sparse_mlpoly.rs
  15. +0
    -282
      src/spartan.rs

+ 3
- 6
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 = []

+ 69
- 117
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 <https://github.com/agl/ed25519/>,
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.
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 <https://github.com/agl/ed25519/>, 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.

+ 225
- 17
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

+ 8
- 11
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)
)

+ 17
- 26
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)
)

+ 12
- 15
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!();
}

+ 13
- 19
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!();
}

+ 5
- 32
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::<Vec<CompressedGroup>>();
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::<Vec<CompressedGroup>>();
PolyCommitment { C }
}
}
pub struct EqPolynomial {
r: Vec<Scalar>,
}
@ -159,7 +142,7 @@ impl DensePolynomial {
)
}
#[cfg(feature = "rayon_par")]
#[cfg(feature = "multicore")]
fn commit_inner(&self, blinds: &Vec<Scalar>, 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");

+ 340
- 3
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<Scalar>, Vec<Scalar>) {
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<Scalar>,
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<u8> = 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<u8> = 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<Scalar>, Vec<Scalar>),
}
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<Scalar>,
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<u8> = 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());
}
}

+ 2
- 0
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)]

+ 27
- 77
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<Scalar>, Vec<Scalar>) {
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
)

+ 8
- 10
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<Scalar>, Vec<Scalar>), 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(

+ 3
- 2
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;

+ 67
- 77
src/sparse_mlpoly.rs

@ -51,18 +51,22 @@ impl Derefs {
pub fn new(row_ops_val: Vec<DensePolynomial>, col_ops_val: Vec<DensePolynomial>) -> 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<Scalar> {
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::<Vec<Scalar>>()
}
pub fn multiply_vec(&self, num_rows: usize, num_cols: usize, z: &[Scalar]) -> Vec<Scalar> {
@ -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<Scalar> = (0..num_vars_y)
.map(|_i| Scalar::random(&mut csprng))
.collect::<Vec<Scalar>>();
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");

+ 0
- 282
src/spartan.rs

@ -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<Scalar>,
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<u8> = 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<u8> = 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<Scalar>, Vec<Scalar>),
}
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<Scalar>,
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<u8> = 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());
}
}

Loading…
Cancel
Save