From 0013f81a6ec25b6d47e9e91c844def8a82344637 Mon Sep 17 00:00:00 2001 From: Srinath Setty Date: Mon, 17 Oct 2022 14:24:35 -0700 Subject: [PATCH] optimize the computation of digest of A/B/C matrices (#55) * optimize the computation of digest of A/B/C matrices * update version * address clippy * address clippy --- Cargo.toml | 2 +- src/dense_mlpoly.rs | 2 +- src/lib.rs | 40 +++++++++++++++++++++++---------------- src/nizk/bullet.rs | 2 +- src/product_tree.rs | 10 +++++----- src/r1csinstance.rs | 40 +++++++++++++++++++-------------------- src/r1csproof.rs | 11 ++++------- src/sparse_mlpoly.rs | 45 +++++++++++++++++--------------------------- 8 files changed, 72 insertions(+), 80 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 095983a..bf5e048 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spartan" -version = "0.7.0" +version = "0.7.1" authors = ["Srinath Setty "] edition = "2021" description = "High-speed zkSNARKs without trusted setup" diff --git a/src/dense_mlpoly.rs b/src/dense_mlpoly.rs index d0ebb34..371b4af 100644 --- a/src/dense_mlpoly.rs +++ b/src/dense_mlpoly.rs @@ -121,7 +121,7 @@ impl IdentityPolynomial { impl DensePolynomial { pub fn new(Z: Vec) -> Self { DensePolynomial { - num_vars: Z.len().log_2() as usize, + num_vars: Z.len().log_2(), len: Z.len(), Z, } diff --git a/src/lib.rs b/src/lib.rs index 27b980d..c43cbb9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,15 +3,15 @@ #![deny(missing_docs)] #![allow(clippy::assertions_on_result_states)] +extern crate ark_std; extern crate byteorder; extern crate core; extern crate digest; +extern crate lazy_static; extern crate merlin; +extern crate rand; extern crate sha3; extern crate test; -extern crate rand; -extern crate lazy_static; -extern crate ark_std; #[macro_use] extern crate json; @@ -37,9 +37,10 @@ mod timer; mod transcript; mod unipoly; - -use core::{cmp::max}; -use std::borrow::Borrow; +use ark_ff::{BigInteger, Field, PrimeField}; +use ark_serialize::*; +use ark_std::{One, UniformRand, Zero}; +use core::cmp::max; use errors::{ProofVerifyError, R1CSError}; use merlin::Transcript; use r1csinstance::{ @@ -48,9 +49,7 @@ use r1csinstance::{ use r1csproof::{R1CSGens, R1CSProof}; use random::RandomTape; use scalar::Scalar; -use ark_serialize::*; -use ark_ff::{PrimeField, Field, BigInteger}; -use ark_std::{One, Zero, UniformRand}; +use std::borrow::Borrow; use timer::Timer; use transcript::{AppendToTranscript, ProofTranscript}; @@ -122,9 +121,11 @@ pub type VarsAssignment = Assignment; pub type InputsAssignment = Assignment; /// `Instance` holds the description of R1CS matrices +/// `Instance` holds the description of R1CS matrices and a hash of the matrices #[derive(Debug)] pub struct Instance { inst: R1CSInstance, + digest: Vec, } impl Instance { @@ -170,7 +171,7 @@ impl Instance { }; let bytes_to_scalar = - |tups: & [(usize, usize, Vec)]| -> Result, R1CSError> { + |tups: &[(usize, usize, Vec)]| -> Result, R1CSError> { let mut mat: Vec<(usize, usize, Scalar)> = Vec::new(); for (row, col, val_bytes) in tups { // row must be smaller than num_cons @@ -232,7 +233,9 @@ impl Instance { &C_scalar.unwrap(), ); - Ok(Instance { inst }) + let digest = inst.get_digest(); + + Ok(Instance { inst, digest }) } /// Checks if a given R1CSInstance is satisfiable with a given variables and inputs assignments @@ -274,8 +277,9 @@ impl Instance { num_inputs: usize, ) -> (Instance, VarsAssignment, InputsAssignment) { let (inst, vars, inputs) = R1CSInstance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); + let digest = inst.get_digest(); ( - Instance { inst }, + Instance { inst, digest }, VarsAssignment { assignment: vars }, InputsAssignment { assignment: inputs }, ) @@ -520,7 +524,7 @@ impl NIZK { let mut random_tape = RandomTape::new(b"proof"); transcript.append_protocol_name(NIZK::protocol_name()); - inst.inst.append_to_transcript(b"inst", transcript); + transcript.append_message(b"R1CSInstanceDigest", &inst.digest); let (r1cs_sat_proof, rx, ry) = { // we might need to pad variables @@ -566,7 +570,7 @@ impl NIZK { let timer_verify = Timer::new("NIZK::verify"); transcript.append_protocol_name(NIZK::protocol_name()); - inst.inst.append_to_transcript(b"inst", transcript); + transcript.append_message(b"R1CSInstanceDigest", &inst.digest); // We send evaluations of A, B, C at r = (rx, ry) as claims // to enable the verifier complete the first sum-check @@ -599,7 +603,7 @@ impl NIZK { #[cfg(test)] mod tests { use super::*; - use ark_ff::{PrimeField}; + use ark_ff::PrimeField; #[test] pub fn check_snark() { @@ -698,7 +702,11 @@ mod tests { A.push((0, num_vars + 2, (Scalar::one().into_repr().to_bytes_le()))); // 1*a B.push((0, num_vars + 2, Scalar::one().into_repr().to_bytes_le())); // 1*a C.push((0, num_vars + 1, Scalar::one().into_repr().to_bytes_le())); // 1*z - C.push((0, num_vars, (-Scalar::from(13u64)).into_repr().to_bytes_le())); // -13*1 + C.push(( + 0, + num_vars, + (-Scalar::from(13u64)).into_repr().to_bytes_le(), + )); // -13*1 C.push((0, num_vars + 3, (-Scalar::one()).into_repr().to_bytes_le())); // -1*b // Var Assignments (Z_0 = 16 is the only output) diff --git a/src/nizk/bullet.rs b/src/nizk/bullet.rs index 6a4cff5..7a2091e 100644 --- a/src/nizk/bullet.rs +++ b/src/nizk/bullet.rs @@ -62,7 +62,7 @@ impl BulletReductionProof { // All of the input vectors must have a length that is a power of two. let mut n = G.len(); assert!(n.is_power_of_two()); - let lg_n = n.log_2() as usize; + let lg_n = n.log_2(); // All of the input vectors must have the same length. assert_eq!(G.len(), n); diff --git a/src/product_tree.rs b/src/product_tree.rs index 1c44941..c129c39 100644 --- a/src/product_tree.rs +++ b/src/product_tree.rs @@ -38,7 +38,7 @@ impl ProductCircuit { let mut left_vec: Vec = Vec::new(); let mut right_vec: Vec = Vec::new(); - let num_layers = poly.len().log_2() as usize; + let num_layers = poly.len().log_2(); let (outp_left, outp_right) = poly.split(poly.len() / 2); left_vec.push(outp_left); @@ -183,7 +183,7 @@ impl ProductCircuitEvalProof { let mut poly_C = DensePolynomial::new(EqPolynomial::new(rand.clone()).evals()); assert_eq!(poly_C.len(), len / 2); - let num_rounds_prod = poly_C.len().log_2() as usize; + let num_rounds_prod = poly_C.len().log_2(); let comb_func_prod = |poly_A_comp: &Scalar, poly_B_comp: &Scalar, poly_C_comp: &Scalar| @@ -224,7 +224,7 @@ impl ProductCircuitEvalProof { len: usize, transcript: &mut Transcript, ) -> (Scalar, Vec) { - let num_layers = len.log_2() as usize; + let num_layers = len.log_2(); let mut claim = eval; let mut rand: Vec = Vec::new(); //let mut num_rounds = 0; @@ -280,7 +280,7 @@ impl ProductCircuitEvalProofBatched { let mut poly_C_par = DensePolynomial::new(EqPolynomial::new(rand.clone()).evals()); assert_eq!(poly_C_par.len(), len / 2); - let num_rounds_prod = poly_C_par.len().log_2() as usize; + let num_rounds_prod = poly_C_par.len().log_2(); let comb_func_prod = |poly_A_comp: &Scalar, poly_B_comp: &Scalar, poly_C_comp: &Scalar| @@ -390,7 +390,7 @@ impl ProductCircuitEvalProofBatched { len: usize, transcript: &mut Transcript, ) -> (Vec, Vec, Vec) { - let num_layers = len.log_2() as usize; + let num_layers = len.log_2(); let mut rand: Vec = Vec::new(); //let mut num_rounds = 0; assert_eq!(self.proof.len(), num_layers); diff --git a/src/r1csinstance.rs b/src/r1csinstance.rs index c144d1c..56bce34 100644 --- a/src/r1csinstance.rs +++ b/src/r1csinstance.rs @@ -10,10 +10,10 @@ use super::sparse_mlpoly::{ SparseMatPolyCommitmentGens, SparseMatPolyEvalProof, SparseMatPolynomial, }; use super::timer::Timer; -use merlin::Transcript; +use ark_ff::Field; use ark_serialize::*; -use ark_std::{One, Zero, UniformRand}; -use ark_ff::{Field}; +use ark_std::{One, UniformRand, Zero}; +use merlin::Transcript; #[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] pub struct R1CSInstance { @@ -25,14 +25,6 @@ pub struct R1CSInstance { C: SparseMatPolynomial, } -impl AppendToTranscript for R1CSInstance { - fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut Transcript) { - let mut bytes = Vec::new(); - self.serialize(&mut bytes).unwrap(); - transcript.append_message(b"R1CSInstance", &bytes); - } -} - pub struct R1CSCommitmentGens { gens: SparseMatPolyCommitmentGens, } @@ -46,8 +38,8 @@ impl R1CSCommitmentGens { num_nz_entries: usize, ) -> R1CSCommitmentGens { assert!(num_inputs < num_vars); - let num_poly_vars_x = num_cons.log_2() as usize; - let num_poly_vars_y = (2 * num_vars).log_2() as usize; + let num_poly_vars_x = num_cons.log_2(); + let num_poly_vars_y = (2 * num_vars).log_2(); let gens = SparseMatPolyCommitmentGens::new(label, num_poly_vars_x, num_poly_vars_y, num_nz_entries, 3); R1CSCommitmentGens { gens } @@ -115,8 +107,8 @@ impl R1CSInstance { assert!(num_inputs < num_vars); // no errors, so create polynomials - let num_poly_vars_x = num_cons.log_2() as usize; - let num_poly_vars_y = (2 * num_vars).log_2() as usize; + let num_poly_vars_x = num_cons.log_2(); + let num_poly_vars_y = (2 * num_vars).log_2(); let mat_A = (0..A.len()) .map(|i| SparseMatEntry::new(A[i].0, A[i].1, A[i].2)) @@ -154,6 +146,12 @@ impl R1CSInstance { self.num_inputs } + pub fn get_digest(&self) -> Vec { + let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default()); + bincode::serialize_into(&mut encoder, &self).unwrap(); + encoder.finish().unwrap() + } + pub fn produce_synthetic_r1cs( num_cons: usize, num_vars: usize, @@ -163,11 +161,11 @@ impl R1CSInstance { Timer::print(&format!("number_of_variables {}", num_vars)); Timer::print(&format!("number_of_inputs {}", num_inputs)); - let mut rng = ark_std::rand::thread_rng(); + let mut rng = ark_std::rand::thread_rng(); // assert num_cons and num_vars are power of 2 - assert_eq!((num_cons.log_2() as usize).pow2(), num_cons); - assert_eq!((num_vars.log_2() as usize).pow2(), num_vars); + assert_eq!((num_cons.log_2()).pow2(), num_cons); + assert_eq!((num_vars.log_2()).pow2(), num_vars); // num_inputs + 1 <= num_vars assert!(num_inputs < num_vars); @@ -214,8 +212,8 @@ impl R1CSInstance { 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.log_2() as usize; - let num_poly_vars_y = (2 * num_vars).log_2() as usize; + let num_poly_vars_x = num_cons.log_2(); + let num_poly_vars_y = (2 * num_vars).log_2(); let poly_A = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, A); let poly_B = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, B); let poly_C = SparseMatPolynomial::new(num_poly_vars_x, num_poly_vars_y, C); @@ -260,7 +258,7 @@ impl R1CSInstance { assert_eq!(Bz.len(), self.num_cons); assert_eq!(Cz.len(), self.num_cons); let res: usize = (0..self.num_cons) - .map(|i| if Az[i] * Bz[i] == Cz[i] { 0 } else { 1 }) + .map(|i| usize::from(Az[i] * Bz[i] != Cz[i])) .sum(); res == 0 diff --git a/src/r1csproof.rs b/src/r1csproof.rs index 3fc3457..91decce 100644 --- a/src/r1csproof.rs +++ b/src/r1csproof.rs @@ -66,7 +66,7 @@ pub struct R1CSGens { impl R1CSGens { pub fn new(label: &'static [u8], _num_cons: usize, num_vars: usize) -> Self { - let num_poly_vars = num_vars.log_2() as usize; + let num_poly_vars = num_vars.log_2(); let gens_pc = PolyCommitmentGens::new(num_poly_vars, label); let gens_sc = R1CSSumcheckGens::new(label, &gens_pc.gens.gens_1); R1CSGens { gens_sc, gens_pc } @@ -155,10 +155,7 @@ impl R1CSProof { }; // derive the verifier's challenge tau - let (num_rounds_x, num_rounds_y) = ( - inst.get_num_cons().log_2() as usize, - z.len().log_2() as usize, - ); + let (num_rounds_x, num_rounds_y) = (inst.get_num_cons().log_2(), z.len().log_2()); let tau = transcript.challenge_vector(b"challenge_tau", num_rounds_x); // compute the initial evaluation table for R(\tau, x) let mut poly_tau = DensePolynomial::new(EqPolynomial::new(tau).evals()); @@ -250,7 +247,7 @@ impl R1CSProof { let n = num_vars; - let (num_rounds_x, num_rounds_y) = (num_cons.log_2() as usize, (2 * num_vars).log_2() as usize); + let (num_rounds_x, num_rounds_y) = (num_cons.log_2(), (2 * num_vars).log_2()); // derive the verifier's challenge tau let tau = transcript.challenge_vector(b"challenge_tau", num_rounds_x); @@ -295,7 +292,7 @@ impl R1CSProof { .map(|i| SparsePolyEntry::new(i + 1, input[i])) .collect::>(), ); - SparsePolynomial::new(n.log_2() as usize, input_as_sparse_poly_entries).evaluate(&ry[1..]) + SparsePolynomial::new(n.log_2(), input_as_sparse_poly_entries).evaluate(&ry[1..]) }; let eval_Z_at_ry = (Scalar::one() - ry[0]) * self.eval_vars_at_ry + ry[0] * poly_input_eval; diff --git a/src/sparse_mlpoly.rs b/src/sparse_mlpoly.rs index 34d4718..bbe7b83 100644 --- a/src/sparse_mlpoly.rs +++ b/src/sparse_mlpoly.rs @@ -90,10 +90,7 @@ impl DerefsEvalProof { transcript: &mut Transcript, random_tape: &mut RandomTape, ) -> PolyEvalProof { - assert_eq!( - joint_poly.get_num_vars(), - r.len() + evals.len().log_2() as usize - ); + assert_eq!(joint_poly.get_num_vars(), r.len() + evals.len().log_2()); // append the claimed evaluations to transcript evals.append_to_transcript(b"evals_ops_val", transcript); @@ -101,7 +98,7 @@ impl DerefsEvalProof { // n-to-1 reduction let (r_joint, eval_joint) = { let challenges = - transcript.challenge_vector(b"challenge_combine_n_to_one", evals.len().log_2() as usize); + transcript.challenge_vector(b"challenge_combine_n_to_one", evals.len().log_2()); let mut poly_evals = DensePolynomial::new(evals); for i in (0..challenges.len()).rev() { poly_evals.bound_poly_var_bot(&challenges[i]); @@ -167,7 +164,7 @@ impl DerefsEvalProof { // n-to-1 reduction let challenges = - transcript.challenge_vector(b"challenge_combine_n_to_one", evals.len().log_2() as usize); + transcript.challenge_vector(b"challenge_combine_n_to_one", evals.len().log_2()); let mut poly_evals = DensePolynomial::new(evals); for i in (0..challenges.len()).rev() { poly_evals.bound_poly_var_bot(&challenges[i]); @@ -301,15 +298,15 @@ impl SparseMatPolyCommitmentGens { num_nz_entries: usize, batch_size: usize, ) -> SparseMatPolyCommitmentGens { - let num_vars_ops = num_nz_entries.next_power_of_two().log_2() as usize - + (batch_size * 5).next_power_of_two().log_2() as usize; + let num_vars_ops = + num_nz_entries.next_power_of_two().log_2() + (batch_size * 5).next_power_of_two().log_2(); 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().log_2() as usize - + (batch_size * 2).next_power_of_two().log_2() as usize; + let num_vars_derefs = + num_nz_entries.next_power_of_two().log_2() + (batch_size * 2).next_power_of_two().log_2(); let gens_ops = PolyCommitmentGens::new(num_vars_ops, label); let gens_mem = PolyCommitmentGens::new(num_vars_mem, label); @@ -779,10 +776,8 @@ impl HashLayerProof { evals_ops.extend(&eval_val_vec); evals_ops.resize(evals_ops.len().next_power_of_two(), Scalar::zero()); evals_ops.append_to_transcript(b"claim_evals_ops", transcript); - let challenges_ops = transcript.challenge_vector( - b"challenge_combine_n_to_one", - evals_ops.len().log_2() as usize, - ); + let challenges_ops = + transcript.challenge_vector(b"challenge_combine_n_to_one", evals_ops.len().log_2()); let mut poly_evals_ops = DensePolynomial::new(evals_ops); for i in (0..challenges_ops.len()).rev() { @@ -808,10 +803,8 @@ impl HashLayerProof { // form a single decommitment using comb_comb_mem at rand_mem let evals_mem: Vec = vec![eval_row_audit_ts, eval_col_audit_ts]; evals_mem.append_to_transcript(b"claim_evals_mem", transcript); - let challenges_mem = transcript.challenge_vector( - b"challenge_combine_two_to_one", - evals_mem.len().log_2() as usize, - ); + let challenges_mem = + transcript.challenge_vector(b"challenge_combine_two_to_one", evals_mem.len().log_2()); let mut poly_evals_mem = DensePolynomial::new(evals_mem); for i in (0..challenges_mem.len()).rev() { @@ -953,10 +946,8 @@ impl HashLayerProof { evals_ops.extend(eval_val_vec); evals_ops.resize(evals_ops.len().next_power_of_two(), Scalar::zero()); evals_ops.append_to_transcript(b"claim_evals_ops", transcript); - let challenges_ops = transcript.challenge_vector( - b"challenge_combine_n_to_one", - evals_ops.len().log_2() as usize, - ); + let challenges_ops = + transcript.challenge_vector(b"challenge_combine_n_to_one", evals_ops.len().log_2()); let mut poly_evals_ops = DensePolynomial::new(evals_ops); for i in (0..challenges_ops.len()).rev() { @@ -979,10 +970,8 @@ impl HashLayerProof { // form a single decommitment using comb_comb_mem at rand_mem let evals_mem: Vec = vec![*eval_row_audit_ts, *eval_col_audit_ts]; evals_mem.append_to_transcript(b"claim_evals_mem", transcript); - let challenges_mem = transcript.challenge_vector( - b"challenge_combine_two_to_one", - evals_mem.len().log_2() as usize, - ); + let challenges_mem = + transcript.challenge_vector(b"challenge_combine_two_to_one", evals_mem.len().log_2()); let mut poly_evals_mem = DensePolynomial::new(evals_mem); for i in (0..challenges_mem.len()).rev() { @@ -1632,8 +1621,8 @@ use rand::RngCore; let num_nz_entries: usize = 256; let num_rows: usize = 256; let num_cols: usize = 256; - let num_vars_x: usize = num_rows.log_2() as usize; - let num_vars_y: usize = num_cols.log_2() as usize; + let num_vars_x: usize = num_rows.log_2(); + let num_vars_y: usize = num_cols.log_2(); let mut M: Vec = Vec::new();