From 456970e710557bb91c03c6e694cc66dcef1ddfe8 Mon Sep 17 00:00:00 2001 From: Srinath Setty Date: Tue, 28 Apr 2020 10:57:11 -0700 Subject: [PATCH] introduce APIs for NIZK and SNARK proof systems (#11) --- benches/commitments.rs | 1 - benches/dotproduct.rs | 1 - benches/spartan.rs | 108 +++++++++++++++++++++++++------ src/profiler.rs | 61 +++++++++++++++--- src/r1csinstance.rs | 4 ++ src/spartan.rs | 140 +++++++++++++++++++++++++++++++++++------ 6 files changed, 265 insertions(+), 50 deletions(-) diff --git a/benches/commitments.rs b/benches/commitments.rs index cc49b4d..ebc6be2 100644 --- a/benches/commitments.rs +++ b/benches/commitments.rs @@ -1,7 +1,6 @@ extern crate byteorder; extern crate core; extern crate criterion; -extern crate curve25519_dalek; extern crate digest; extern crate libspartan; extern crate merlin; diff --git a/benches/dotproduct.rs b/benches/dotproduct.rs index 3d03537..1aa4cd8 100644 --- a/benches/dotproduct.rs +++ b/benches/dotproduct.rs @@ -1,7 +1,6 @@ extern crate byteorder; extern crate core; extern crate criterion; -extern crate curve25519_dalek; extern crate digest; extern crate libspartan; extern crate merlin; diff --git a/benches/spartan.rs b/benches/spartan.rs index b2e6a76..6fc06cb 100644 --- a/benches/spartan.rs +++ b/benches/spartan.rs @@ -10,15 +10,15 @@ extern crate sha3; use libspartan::math::Math; use libspartan::r1csinstance::{R1CSCommitmentGens, R1CSInstance}; use libspartan::r1csproof::R1CSGens; -use libspartan::spartan::{SpartanGens, SpartanProof}; +use libspartan::spartan::{NIZKGens, SNARKGens, NIZK, SNARK}; use merlin::Transcript; use criterion::*; -fn encode_benchmark(c: &mut Criterion) { +fn snark_encode_benchmark(c: &mut Criterion) { for &s in [10, 12, 16].iter() { let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); - let mut group = c.benchmark_group("spartan_encode_benchmark"); + let mut group = c.benchmark_group("SNARK_encode_benchmark"); group.plot_config(plot_config); let num_vars = s.pow2(); @@ -27,25 +27,23 @@ fn encode_benchmark(c: &mut Criterion) { let (inst, _vars, _input) = R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); let n = inst.get_num_vars(); - let m = n.square_root(); - assert_eq!(n, m * m); let r1cs_size = inst.size(); let gens_r1cs = R1CSCommitmentGens::new(&r1cs_size, b"gens_r1cs"); - let name = format!("spartan_encode_{}", n); + let name = format!("SNARK_encode_{}", n); group.bench_function(&name, move |b| { b.iter(|| { - SpartanProof::encode(black_box(&inst), black_box(&gens_r1cs)); + SNARK::encode(black_box(&inst), black_box(&gens_r1cs)); }); }); group.finish(); } } -fn prove_benchmark(c: &mut Criterion) { +fn snark_prove_benchmark(c: &mut Criterion) { for &s in [10, 12, 16].iter() { let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); - let mut group = c.benchmark_group("spartan_prove_benchmark"); + let mut group = c.benchmark_group("SNARK_prove_benchmark"); group.plot_config(plot_config); let num_vars = s.pow2(); @@ -60,14 +58,14 @@ fn prove_benchmark(c: &mut Criterion) { let gens_r1cs_sat = R1CSGens::new(num_cons, num_vars, b"gens_r1cs_sat"); // produce a proof of satisfiability - let (_comm, decomm) = SpartanProof::encode(&inst, &gens_r1cs_eval); - let gens = SpartanGens::new(gens_r1cs_sat, gens_r1cs_eval); + let (_comm, decomm) = SNARK::encode(&inst, &gens_r1cs_eval); + let gens = SNARKGens::new(gens_r1cs_sat, gens_r1cs_eval); - let name = format!("spartan_prove_{}", n); + let name = format!("SNARK_prove_{}", n); group.bench_function(&name, move |b| { b.iter(|| { let mut prover_transcript = Transcript::new(b"example"); - SpartanProof::prove( + SNARK::prove( black_box(&inst), black_box(&decomm), black_box(vars.clone()), @@ -81,10 +79,10 @@ fn prove_benchmark(c: &mut Criterion) { } } -fn verify_benchmark(c: &mut Criterion) { +fn snark_verify_benchmark(c: &mut Criterion) { for &s in [10, 12, 16].iter() { let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); - let mut group = c.benchmark_group("spartan_verify_benchmark"); + let mut group = c.benchmark_group("SNARK_verify_benchmark"); group.plot_config(plot_config); let num_vars = s.pow2(); @@ -97,16 +95,16 @@ fn verify_benchmark(c: &mut Criterion) { let gens_r1cs_eval = R1CSCommitmentGens::new(&r1cs_size, b"gens_r1cs_eval"); // create a commitment to R1CSInstance - let (comm, decomm) = SpartanProof::encode(&inst, &gens_r1cs_eval); + let (comm, decomm) = SNARK::encode(&inst, &gens_r1cs_eval); let gens_r1cs_sat = R1CSGens::new(num_cons, num_vars, b"gens_r1cs_sat"); - let gens = SpartanGens::new(gens_r1cs_sat, gens_r1cs_eval); + let gens = SNARKGens::new(gens_r1cs_sat, gens_r1cs_eval); // produce a proof of satisfiability let mut prover_transcript = Transcript::new(b"example"); - let proof = SpartanProof::prove(&inst, &decomm, vars, &input, &gens, &mut prover_transcript); + let proof = SNARK::prove(&inst, &decomm, vars, &input, &gens, &mut prover_transcript); - let name = format!("spartan_verify_{}", n); + let name = format!("SNARK_verify_{}", n); group.bench_function(&name, move |b| { b.iter(|| { let mut verifier_transcript = Transcript::new(b"example"); @@ -124,6 +122,76 @@ fn verify_benchmark(c: &mut Criterion) { } } +fn nizk_prove_benchmark(c: &mut Criterion) { + for &s in [10, 12, 16].iter() { + let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); + let mut group = c.benchmark_group("NIZK_prove_benchmark"); + group.plot_config(plot_config); + + let num_vars = s.pow2(); + 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 gens_r1cs_sat = R1CSGens::new(num_cons, num_vars, b"gens_r1cs_sat"); + let gens = NIZKGens::new(gens_r1cs_sat); + + let name = format!("NIZK_prove_{}", n); + 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(&gens), + black_box(&mut prover_transcript), + ); + }); + }); + group.finish(); + } +} + +fn nizk_verify_benchmark(c: &mut Criterion) { + for &s in [10, 12, 16].iter() { + let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic); + let mut group = c.benchmark_group("NIZK_verify_benchmark"); + group.plot_config(plot_config); + + let num_vars = s.pow2(); + 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 gens_r1cs_sat = R1CSGens::new(num_cons, num_vars, b"gens_r1cs_sat"); + let gens = NIZKGens::new(gens_r1cs_sat); + + // 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 name = format!("NIZK_verify_{}", n); + 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(&mut verifier_transcript), + black_box(&gens) + ) + .is_ok()); + }); + }); + group.finish(); + } +} + fn set_duration() -> Criterion { Criterion::default().sample_size(10) // .measurement_time(Duration::new(0, 50000000)) @@ -132,7 +200,7 @@ fn set_duration() -> Criterion { criterion_group! { name = benches_spartan; config = set_duration(); -targets = encode_benchmark, prove_benchmark, verify_benchmark +targets = snark_encode_benchmark, snark_prove_benchmark, snark_verify_benchmark } criterion_main!(benches_spartan); diff --git a/src/profiler.rs b/src/profiler.rs index fddc8d0..92999ec 100644 --- a/src/profiler.rs +++ b/src/profiler.rs @@ -8,42 +8,48 @@ use flate2::{write::ZlibEncoder, Compression}; use libspartan::math::Math; use libspartan::r1csinstance::{R1CSCommitmentGens, R1CSInstance}; use libspartan::r1csproof::R1CSGens; -use libspartan::spartan::{SpartanGens, SpartanProof}; +use libspartan::spartan::{NIZKGens, SNARKGens, NIZK, SNARK}; use libspartan::timer::Timer; use merlin::Transcript; pub fn main() { - for &s in [12, 16, 20].iter() { + // the list of number of variables (and constraints) in an R1CS instance + let inst_sizes = vec![12, 16, 20]; + + println!("Profiler:: SNARK"); + for &s in inst_sizes.iter() { let num_vars = (s as usize).pow2(); 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 r1cs_size = inst.size(); let gens_r1cs_eval = R1CSCommitmentGens::new(&r1cs_size, b"gens_r1cs_eval"); - Timer::print(&format!("number_of_constraints {}", num_cons)); // create a commitment to R1CSInstance - let timer_encode = Timer::new("SpartanProof::encode"); - let (comm, decomm) = SpartanProof::encode(&inst, &gens_r1cs_eval); + let timer_encode = Timer::new("SNARK::encode"); + let (comm, decomm) = SNARK::encode(&inst, &gens_r1cs_eval); timer_encode.stop(); let gens_r1cs_sat = R1CSGens::new(num_cons, num_vars, b"gens_r1cs_sat"); - let gens = SpartanGens::new(gens_r1cs_sat, gens_r1cs_eval); + let gens = SNARKGens::new(gens_r1cs_sat, gens_r1cs_eval); // produce a proof of satisfiability - let timer_prove = Timer::new("SpartanProof::prove"); + let timer_prove = Timer::new("SNARK::prove"); let mut prover_transcript = Transcript::new(b"example"); - let proof = SpartanProof::prove(&inst, &decomm, vars, &input, &gens, &mut prover_transcript); + let proof = SNARK::prove(&inst, &decomm, vars, &input, &gens, &mut prover_transcript); timer_prove.stop(); 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!("proof_compressed_len {:?}", proof_encoded.len()); + let msg_proof_len = format!("SNARK::proof_compressed_len {:?}", proof_encoded.len()); Timer::print(&msg_proof_len); - let timer_verify = Timer::new("SpartanProof::verify"); + // verify the proof of satisfiability + let timer_verify = Timer::new("SNARK::verify"); let mut verifier_transcript = Transcript::new(b"example"); assert!(proof .verify(&comm, &input, &mut verifier_transcript, &gens) @@ -52,4 +58,39 @@ pub fn main() { println!(); } + + println!("Profiler:: NIZK"); + for &s in inst_sizes.iter() { + let num_vars = (s as usize).pow2(); + 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 gens_r1cs_sat = R1CSGens::new(num_cons, num_vars, b"gens_r1cs_sat"); + let gens = NIZKGens::new(gens_r1cs_sat); + + // 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 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); + + // verify the proof of satisfiability + let timer_verify = Timer::new("NIZK::verify"); + let mut verifier_transcript = Transcript::new(b"example"); + assert!(proof + .verify(&inst, &input, &mut verifier_transcript, &gens) + .is_ok()); + timer_verify.stop(); + + println!(); + } } diff --git a/src/r1csinstance.rs b/src/r1csinstance.rs index a04928b..bb769b0 100644 --- a/src/r1csinstance.rs +++ b/src/r1csinstance.rs @@ -117,6 +117,10 @@ impl R1CSInstance { self.num_cons } + pub fn get_num_inputs(&self) -> usize { + self.num_inputs + } + pub fn size(&self) -> R1CSInstanceSize { R1CSInstanceSize { size_A: self.A.size(), diff --git a/src/spartan.rs b/src/spartan.rs index adc5feb..9fe926e 100644 --- a/src/spartan.rs +++ b/src/spartan.rs @@ -12,14 +12,14 @@ use merlin::Transcript; use rand::rngs::OsRng; use serde::{Deserialize, Serialize}; -pub struct SpartanGens { +pub struct SNARKGens { gens_r1cs_sat: R1CSGens, gens_r1cs_eval: R1CSCommitmentGens, } -impl SpartanGens { - pub fn new(gens_r1cs_sat: R1CSGens, gens_r1cs_eval: R1CSCommitmentGens) -> SpartanGens { - SpartanGens { +impl SNARKGens { + pub fn new(gens_r1cs_sat: R1CSGens, gens_r1cs_eval: R1CSCommitmentGens) -> Self { + SNARKGens { gens_r1cs_sat, gens_r1cs_eval, } @@ -27,15 +27,15 @@ impl SpartanGens { } #[derive(Serialize, Deserialize, Debug)] -pub struct SpartanProof { +pub struct SNARK { r1cs_sat_proof: R1CSProof, inst_evals: R1CSInstanceEvals, r1cs_eval_proof: R1CSEvalProof, } -impl SpartanProof { +impl SNARK { fn protocol_name() -> &'static [u8] { - b"Spartan proof" + b"Spartan SNARK proof" } /// A public computation to create a commitment to an R1CS instance @@ -52,19 +52,19 @@ impl SpartanProof { decomm: &R1CSDecommitment, vars: Vec, input: &Vec, - gens: &SpartanGens, + gens: &SNARKGens, transcript: &mut Transcript, - ) -> SpartanProof { + ) -> Self { // we create a Transcript object seeded with a random Scalar // to aid the prover produce its randomness let mut random_tape = { let mut csprng: OsRng = OsRng; - let mut tape = Transcript::new(b"SpartanProof"); + let mut tape = Transcript::new(b"SpartanSNARKProof"); tape.append_scalar(b"init_randomness", &Scalar::random(&mut csprng)); tape }; - transcript.append_protocol_name(SpartanProof::protocol_name()); + transcript.append_protocol_name(SNARK::protocol_name()); let (r1cs_sat_proof, rx, ry) = { let (proof, rx, ry) = R1CSProof::prove( inst, @@ -107,7 +107,7 @@ impl SpartanProof { proof }; - SpartanProof { + SNARK { r1cs_sat_proof, inst_evals, r1cs_eval_proof, @@ -120,9 +120,9 @@ impl SpartanProof { comm: &R1CSCommitment, input: &Vec, transcript: &mut Transcript, - gens: &SpartanGens, + gens: &SNARKGens, ) -> Result<(), ProofVerifyError> { - transcript.append_protocol_name(SpartanProof::protocol_name()); + transcript.append_protocol_name(SNARK::protocol_name()); let timer_sat_proof = Timer::new("verify_sat_proof"); assert_eq!(input.len(), comm.get_num_inputs()); @@ -159,12 +159,116 @@ impl SpartanProof { } } +pub struct NIZKGens { + gens_r1cs_sat: R1CSGens, +} + +impl NIZKGens { + pub fn new(gens_r1cs_sat: R1CSGens) -> Self { + 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: &Vec, + 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 = { + let mut csprng: OsRng = OsRng; + let mut tape = Transcript::new(b"SpartanNIZKProof"); + tape.append_scalar(b"init_randomness", &Scalar::random(&mut csprng)); + tape + }; + + 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: &Vec, + 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_spartan_proof() { + pub fn check_snark() { let num_vars = 256; let num_cons = num_vars; let num_inputs = 10; @@ -174,13 +278,13 @@ mod tests { let gens_r1cs_eval = R1CSCommitmentGens::new(&r1cs_size, b"gens_r1cs_eval"); // create a commitment to R1CSInstance - let (comm, decomm) = SpartanProof::encode(&inst, &gens_r1cs_eval); + let (comm, decomm) = SNARK::encode(&inst, &gens_r1cs_eval); let gens_r1cs_sat = R1CSGens::new(num_cons, num_vars, b"gens_r1cs_sat"); - let gens = SpartanGens::new(gens_r1cs_sat, gens_r1cs_eval); + let gens = SNARKGens::new(gens_r1cs_sat, gens_r1cs_eval); let mut prover_transcript = Transcript::new(b"example"); - let proof = SpartanProof::prove(&inst, &decomm, vars, &input, &gens, &mut prover_transcript); + let proof = SNARK::prove(&inst, &decomm, vars, &input, &gens, &mut prover_transcript); let mut verifier_transcript = Transcript::new(b"example"); assert!(proof