diff --git a/benches/compressed-snark.rs b/benches/compressed-snark.rs index 3efabcdb4..84ceb8469 100644 --- a/benches/compressed-snark.rs +++ b/benches/compressed-snark.rs @@ -38,13 +38,13 @@ cfg_if::cfg_if! { criterion_group! { name = compressed_snark; config = Criterion::default().warm_up_time(Duration::from_millis(3000)).with_profiler(pprof::criterion::PProfProfiler::new(100, pprof::criterion::Output::Flamegraph(None))); - targets = bench_compressed_snark, bench_compressed_snark_with_computational_commitments, bench_compressed_batched_snark, bench_compressed_batched_snark_with_computational_commitments + targets = bench_compressed_snark, bench_compressed_snark_with_computational_commitments, } } else { criterion_group! { name = compressed_snark; config = Criterion::default().warm_up_time(Duration::from_millis(3000)); - targets = bench_compressed_snark, bench_compressed_snark_with_computational_commitments, bench_compressed_batched_snark, bench_compressed_batched_snark_with_computational_commitments + targets = bench_compressed_snark, bench_compressed_snark_with_computational_commitments, } } } @@ -188,65 +188,6 @@ fn bench_compressed_snark_with_computational_commitments(c: &mut Criterion) { } } -// SNARKs without computation commitmnets -type BS1 = arecibo::spartan::batched::BatchedRelaxedR1CSSNARK; -type BS2 = arecibo::spartan::batched::BatchedRelaxedR1CSSNARK; -// SNARKs with computation commitmnets -type BSS1 = arecibo::spartan::batched_ppsnark::BatchedRelaxedR1CSSNARK; -type BSS2 = arecibo::spartan::batched_ppsnark::BatchedRelaxedR1CSSNARK; - -fn bench_compressed_batched_snark(c: &mut Criterion) { - // we vary the number of constraints in the step circuit - for &num_cons_in_augmented_circuit in [ - NUM_CONS_VERIFIER_CIRCUIT_PRIMARY, - 16384, - 32768, - 65536, - 131072, - 262144, - ] - .iter() - { - // number of constraints in the step circuit - let num_cons = num_cons_in_augmented_circuit - NUM_CONS_VERIFIER_CIRCUIT_PRIMARY; - - let mut group = c.benchmark_group("BatchedCompressedSNARK"); - group.sampling_mode(SamplingMode::Flat); - group.sample_size(NUM_SAMPLES); - group.noise_threshold(noise_threshold_env().unwrap_or(0.05)); - - bench_compressed_snark_internal::(&mut group, num_cons); - - group.finish(); - } -} - -fn bench_compressed_batched_snark_with_computational_commitments(c: &mut Criterion) { - // we vary the number of constraints in the step circuit - for &num_cons_in_augmented_circuit in [ - NUM_CONS_VERIFIER_CIRCUIT_PRIMARY, - 16384, - 32768, - 65536, - 131072, - 262144, - ] - .iter() - { - // number of constraints in the step circuit - let num_cons = num_cons_in_augmented_circuit - NUM_CONS_VERIFIER_CIRCUIT_PRIMARY; - - let mut group = c.benchmark_group("BatchedCompressedSNARK-Commitments"); - group.sampling_mode(SamplingMode::Flat); - group.sample_size(NUM_SAMPLES); - group.noise_threshold(noise_threshold_env().unwrap_or(0.05)); - - bench_compressed_snark_internal::(&mut group, num_cons); - - group.finish(); - } -} - #[derive(Clone, Debug, Default)] struct NonTrivialCircuit { num_cons: usize, diff --git a/src/spartan/mod.rs b/src/spartan/mod.rs index 9b38adb24..46a598b31 100644 --- a/src/spartan/mod.rs +++ b/src/spartan/mod.rs @@ -23,7 +23,6 @@ use crate::{ }; use ff::Field; use itertools::Itertools as _; -use polys::multilinear::SparsePolynomial; use rayon::{iter::IntoParallelRefIterator, prelude::*}; use rayon_scan::ScanParallelIterator as _; use ref_cast::RefCast; diff --git a/src/spartan/polys/power.rs b/src/spartan/polys/power.rs index 55bd2a4ad..7b1eb0b0a 100644 --- a/src/spartan/polys/power.rs +++ b/src/spartan/polys/power.rs @@ -53,13 +53,6 @@ impl PowPolynomial { pub fn coordinates(self) -> Vec { self.eq.r } - - /// Evaluates the `PowPolynomial` at all the `2^|t_pow|` points in its domain. - /// - /// Returns a vector of Scalars, each corresponding to the polynomial evaluation at a specific point. - pub fn evals(&self) -> Vec { - self.eq.evals() - } } impl From> for EqPolynomial { diff --git a/src/spartan/ppsnark.rs b/src/spartan/ppsnark.rs index 711f80039..66f14eea5 100644 --- a/src/spartan/ppsnark.rs +++ b/src/spartan/ppsnark.rs @@ -4,51 +4,14 @@ //! polynomial commitment scheme in which the verifier's costs is succinct. //! This code includes experimental optimizations to reduce runtimes and proof sizes. use crate::{ - digest::{DigestComputer, SimpleDigestible}, - errors::NovaError, - r1cs::{R1CSShape, RelaxedR1CSInstance, RelaxedR1CSWitness}, - spartan::{ - math::Math, - polys::{ - eq::EqPolynomial, - identity::IdentityPolynomial, - multilinear::MultilinearPolynomial, - power::PowPolynomial, - univariate::{CompressedUniPoly, UniPoly}, - }, - powers, - sumcheck::{ - engine::{ - InnerSumcheckInstance, MemorySumcheckInstance, OuterSumcheckInstance, SumcheckEngine, - WitnessBoundSumcheck, - }, - SumcheckProof, - }, - PolyEvalInstance, PolyEvalWitness, SparsePolynomial, - }, - traits::{ - commitment::{CommitmentEngineTrait, CommitmentTrait, Len}, - evaluation::EvaluationEngineTrait, - snark::{DigestHelperTrait, RelaxedR1CSSNARKTrait}, - Engine, TranscriptEngineTrait, TranscriptReprTrait, - }, - zip_with, Commitment, CommitmentKey, CompressedCommitment, + r1cs::R1CSShape, + traits::{commitment::CommitmentEngineTrait, Engine, TranscriptReprTrait}, + Commitment, CommitmentKey, }; use core::cmp::max; use ff::Field; -use itertools::Itertools as _; -use once_cell::sync::OnceCell; use rayon::prelude::*; use serde::{Deserialize, Serialize}; -use std::sync::Arc; - -use super::polys::masked_eq::MaskedEqPolynomial; - -fn padded(v: &[E::Scalar], n: usize, e: &E::Scalar) -> Vec { - let mut v_padded = vec![*e; n]; - v_padded[..v.len()].copy_from_slice(v); - v_padded -} /// A type that holds `R1CSShape` in a form amenable to memory checking #[derive(Debug, Clone, Serialize, Deserialize)] @@ -206,869 +169,14 @@ impl R1CSShapeSparkRepr { comm_ts_col: comm_vec[6], } } - - // computes evaluation oracles - fn evaluation_oracles( - &self, - S: &R1CSShape, - r_x: &E::Scalar, - z: &[E::Scalar], - ) -> ( - Vec, - Vec, - Vec, - Vec, - ) { - let mem_row = PowPolynomial::new(r_x, self.N.log_2()).evals(); - let mem_col = padded::(z, self.N, &E::Scalar::ZERO); - - let (L_row, L_col) = { - let mut L_row = vec![mem_row[0]; self.N]; // we place mem_row[0] since resized row is appended with 0s - let mut L_col = vec![mem_col[self.N - 1]; self.N]; // we place mem_col[N-1] since resized col is appended with N-1 - - for (i, (val_r, val_c)) in S - .A - .iter() - .chain(S.B.iter()) - .chain(S.C.iter()) - .map(|(r, c, _)| (mem_row[r], mem_col[c])) - .enumerate() - { - L_row[i] = val_r; - L_col[i] = val_c; - } - (L_row, L_col) - }; - - (mem_row, mem_col, L_row, L_col) - } } /// A type that represents the prover's key -#[derive(Debug, Clone)] -pub struct ProverKey> { - pk_ee: EE::ProverKey, - S_repr: R1CSShapeSparkRepr, - S_comm: R1CSShapeSparkCommitment, - vk_digest: E::Scalar, // digest of verifier's key -} - +pub type ProverKey = crate::spartan::batched_ppsnark::ProverKey; /// A type that represents the verifier's key -#[derive(Debug, Clone, Serialize)] -#[serde(bound = "EE::VerifierKey: Serialize")] -pub struct VerifierKey> { - num_cons: usize, - num_vars: usize, - vk_ee: EE::VerifierKey, - S_comm: R1CSShapeSparkCommitment, - #[serde(skip, default = "OnceCell::new")] - digest: OnceCell, -} - -impl> SimpleDigestible for VerifierKey where - EE::VerifierKey: Serialize -{ -} +pub type VerifierKey = crate::spartan::batched_ppsnark::VerifierKey; /// A succinct proof of knowledge of a witness to a relaxed R1CS instance /// The proof is produced using Spartan's combination of the sum-check and /// the commitment to a vector viewed as a polynomial commitment -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(bound = "")] -pub struct RelaxedR1CSSNARK> { - // commitment to oracles: the first three are for Az, Bz, Cz, - // and the last two are for memory reads - comm_Az: CompressedCommitment, - comm_Bz: CompressedCommitment, - comm_Cz: CompressedCommitment, - comm_L_row: CompressedCommitment, - comm_L_col: CompressedCommitment, - - // commitments to aid the memory checks - comm_t_plus_r_inv_row: CompressedCommitment, - comm_w_plus_r_inv_row: CompressedCommitment, - comm_t_plus_r_inv_col: CompressedCommitment, - comm_w_plus_r_inv_col: CompressedCommitment, - - // claims about Az, Bz, and Cz polynomials - eval_Az_at_tau: E::Scalar, - eval_Bz_at_tau: E::Scalar, - eval_Cz_at_tau: E::Scalar, - - // sum-check - sc: SumcheckProof, - - // claims from the end of sum-check - eval_Az: E::Scalar, - eval_Bz: E::Scalar, - eval_Cz: E::Scalar, - eval_E: E::Scalar, - eval_L_row: E::Scalar, - eval_L_col: E::Scalar, - eval_val_A: E::Scalar, - eval_val_B: E::Scalar, - eval_val_C: E::Scalar, - - eval_W: E::Scalar, - - eval_t_plus_r_inv_row: E::Scalar, - eval_row: E::Scalar, // address - eval_w_plus_r_inv_row: E::Scalar, - eval_ts_row: E::Scalar, - - eval_t_plus_r_inv_col: E::Scalar, - eval_col: E::Scalar, // address - eval_w_plus_r_inv_col: E::Scalar, - eval_ts_col: E::Scalar, - - // a PCS evaluation argument - eval_arg: EE::EvaluationArgument, -} - -impl> RelaxedR1CSSNARK { - fn prove_helper( - mem: &mut T1, - outer: &mut T2, - inner: &mut T3, - witness: &mut T4, - transcript: &mut E::TE, - ) -> Result< - ( - SumcheckProof, - Vec, - Vec>, - Vec>, - Vec>, - Vec>, - ), - NovaError, - > - where - T1: SumcheckEngine, - T2: SumcheckEngine, - T3: SumcheckEngine, - T4: SumcheckEngine, - { - // sanity checks - assert_eq!(mem.size(), outer.size()); - assert_eq!(mem.size(), inner.size()); - assert_eq!(mem.size(), witness.size()); - assert_eq!(mem.degree(), outer.degree()); - assert_eq!(mem.degree(), inner.degree()); - assert_eq!(mem.degree(), witness.degree()); - - // these claims are already added to the transcript, so we do not need to add - let claims = mem - .initial_claims() - .into_iter() - .chain(outer.initial_claims()) - .chain(inner.initial_claims()) - .chain(witness.initial_claims()) - .collect::>(); - - let s = transcript.squeeze(b"r")?; - let coeffs = powers(&s, claims.len()); - - // compute the joint claim - let claim = zip_with!(iter, (claims, coeffs), |c_1, c_2| *c_1 * c_2).sum(); - - let mut e = claim; - let mut r: Vec = Vec::new(); - let mut cubic_polys: Vec> = Vec::new(); - let num_rounds = mem.size().log_2(); - for _ in 0..num_rounds { - let ((evals_mem, evals_outer), (evals_inner, evals_witness)) = rayon::join( - || rayon::join(|| mem.evaluation_points(), || outer.evaluation_points()), - || rayon::join(|| inner.evaluation_points(), || witness.evaluation_points()), - ); - - let evals: Vec> = evals_mem - .into_iter() - .chain(evals_outer.into_iter()) - .chain(evals_inner.into_iter()) - .chain(evals_witness.into_iter()) - .collect::>>(); - assert_eq!(evals.len(), claims.len()); - - let evals_combined_0 = (0..evals.len()).map(|i| evals[i][0] * coeffs[i]).sum(); - let evals_combined_2 = (0..evals.len()).map(|i| evals[i][1] * coeffs[i]).sum(); - let evals_combined_3 = (0..evals.len()).map(|i| evals[i][2] * coeffs[i]).sum(); - - let evals = vec![ - evals_combined_0, - e - evals_combined_0, - evals_combined_2, - evals_combined_3, - ]; - let poly = UniPoly::from_evals(&evals); - - // append the prover's message to the transcript - transcript.absorb(b"p", &poly); - - // derive the verifier's challenge for the next round - let r_i = transcript.squeeze(b"c")?; - r.push(r_i); - - let _ = rayon::join( - || rayon::join(|| mem.bound(&r_i), || outer.bound(&r_i)), - || rayon::join(|| inner.bound(&r_i), || witness.bound(&r_i)), - ); - - e = poly.evaluate(&r_i); - cubic_polys.push(poly.compress()); - } - - let mem_claims = mem.final_claims(); - let outer_claims = outer.final_claims(); - let inner_claims = inner.final_claims(); - let witness_claims = witness.final_claims(); - - Ok(( - SumcheckProof::new(cubic_polys), - r, - mem_claims, - outer_claims, - inner_claims, - witness_claims, - )) - } -} - -impl> VerifierKey { - fn new( - num_cons: usize, - num_vars: usize, - S_comm: R1CSShapeSparkCommitment, - vk_ee: EE::VerifierKey, - ) -> Self { - Self { - num_cons, - num_vars, - S_comm, - vk_ee, - digest: Default::default(), - } - } -} -impl> DigestHelperTrait for VerifierKey { - /// Returns the digest of the verifier's key - fn digest(&self) -> E::Scalar { - self - .digest - .get_or_try_init(|| { - let dc = DigestComputer::new(self); - dc.digest() - }) - .cloned() - .expect("Failure to retrieve digest!") - } -} - -impl> RelaxedR1CSSNARKTrait for RelaxedR1CSSNARK { - type ProverKey = ProverKey; - type VerifierKey = VerifierKey; - - fn ck_floor() -> Box Fn(&'a R1CSShape) -> usize> { - Box::new(|shape: &R1CSShape| -> usize { - // the commitment key should be large enough to commit to the R1CS matrices - shape.A.len() + shape.B.len() + shape.C.len() - }) - } - - fn setup( - ck: Arc>, - S: &R1CSShape, - ) -> Result<(Self::ProverKey, Self::VerifierKey), NovaError> { - // check the provided commitment key meets minimal requirements - if ck.length() < Self::ck_floor()(S) { - return Err(NovaError::InvalidCommitmentKeyLength); - } - let (pk_ee, vk_ee) = EE::setup(ck.clone()); - - // pad the R1CS matrices - let S = S.pad(); - - let S_repr = R1CSShapeSparkRepr::new(&S); - let S_comm = S_repr.commit(&*ck); - - let vk = VerifierKey::new(S.num_cons, S.num_vars, S_comm.clone(), vk_ee); - - let pk = ProverKey { - pk_ee, - S_repr, - S_comm, - vk_digest: vk.digest(), - }; - - Ok((pk, vk)) - } - - /// produces a succinct proof of satisfiability of a `RelaxedR1CS` instance - #[tracing::instrument(skip_all, name = "PPSNARK::prove")] - fn prove( - ck: &CommitmentKey, - pk: &Self::ProverKey, - S: &R1CSShape, - U: &RelaxedR1CSInstance, - W: &RelaxedR1CSWitness, - ) -> Result { - // pad the R1CSShape - let S = S.pad(); - // sanity check that R1CSShape has all required size characteristics - assert!(S.is_regular_shape()); - - let W = W.pad(&S); // pad the witness - let mut transcript = E::TE::new(b"RelaxedR1CSSNARK"); - - // append the verifier key (which includes commitment to R1CS matrices) and the RelaxedR1CSInstance to the transcript - transcript.absorb(b"vk", &pk.vk_digest); - transcript.absorb(b"U", U); - - // compute the full satisfying assignment by concatenating W.W, U.u, and U.X - let z = [W.W.clone(), vec![U.u], U.X.clone()].concat(); - - // compute Az, Bz, Cz - let (mut Az, mut Bz, mut Cz) = S.multiply_vec(&z)?; - - // commit to Az, Bz, Cz - let (comm_Az, (comm_Bz, comm_Cz)) = rayon::join( - || E::CE::commit(ck, &Az), - || rayon::join(|| E::CE::commit(ck, &Bz), || E::CE::commit(ck, &Cz)), - ); - - transcript.absorb(b"c", &[comm_Az, comm_Bz, comm_Cz].as_slice()); - - // number of rounds of sum-check - let num_rounds_sc = pk.S_repr.N.log_2(); - let tau = transcript.squeeze(b"t")?; - let tau_coords = PowPolynomial::new(&tau, num_rounds_sc).coordinates(); - - // (1) send commitments to Az, Bz, and Cz along with their evaluations at tau - let (Az, Bz, Cz, W, E) = { - Az.resize(pk.S_repr.N, E::Scalar::ZERO); - Bz.resize(pk.S_repr.N, E::Scalar::ZERO); - Cz.resize(pk.S_repr.N, E::Scalar::ZERO); - let E = padded::(&W.E, pk.S_repr.N, &E::Scalar::ZERO); - let W = padded::(&W.W, pk.S_repr.N, &E::Scalar::ZERO); - - (Az, Bz, Cz, W, E) - }; - let chis_taus = EqPolynomial::evals_from_points(&tau_coords); - let (eval_Az_at_tau, eval_Bz_at_tau, eval_Cz_at_tau) = { - let evals_at_tau = [&Az, &Bz, &Cz] - .into_par_iter() - .map(|p| MultilinearPolynomial::evaluate_with_chis(p, &chis_taus)) - .collect::>(); - (evals_at_tau[0], evals_at_tau[1], evals_at_tau[2]) - }; - - // (2) send commitments to the following two oracles - // L_row(i) = eq(tau, row(i)) for all i - // L_col(i) = z(col(i)) for all i - let (mem_row, mem_col, L_row, L_col) = pk.S_repr.evaluation_oracles(&S, &tau, &z); - let (comm_L_row, comm_L_col) = - rayon::join(|| E::CE::commit(ck, &L_row), || E::CE::commit(ck, &L_col)); - - // since all the three polynomials are opened at tau, - // we can combine them into a single polynomial opened at tau - let eval_vec = vec![eval_Az_at_tau, eval_Bz_at_tau, eval_Cz_at_tau]; - - // absorb the claimed evaluations into the transcript - transcript.absorb(b"e", &eval_vec.as_slice()); - // absorb commitments to L_row and L_col in the transcript - transcript.absorb(b"e", &vec![comm_L_row, comm_L_col].as_slice()); - let comm_vec = vec![comm_Az, comm_Bz, comm_Cz]; - let poly_vec = vec![&Az, &Bz, &Cz]; - let c = transcript.squeeze(b"c")?; - let w: PolyEvalWitness = PolyEvalWitness::batch(&poly_vec, &c); - let u: PolyEvalInstance = - PolyEvalInstance::batch(&comm_vec, tau_coords.clone(), &eval_vec, &c); - - // we now need to prove three claims - // (1) 0 = \sum_x poly_tau(x) * (poly_Az(x) * poly_Bz(x) - poly_uCz_E(x)), and eval_Az_at_tau + r * eval_Bz_at_tau + r^2 * eval_Cz_at_tau = (Az+r*Bz+r^2*Cz)(tau) - // (2) eval_Az_at_tau + c * eval_Bz_at_tau + c^2 * eval_Cz_at_tau = \sum_y L_row(y) * (val_A(y) + c * val_B(y) + c^2 * val_C(y)) * L_col(y) - // (3) L_row(i) = eq(tau, row(i)) and L_col(i) = z(col(i)) - let gamma = transcript.squeeze(b"g")?; - let r = transcript.squeeze(b"r")?; - - let ((mut outer_sc_inst, mut inner_sc_inst), mem_res) = rayon::join( - || { - // a sum-check instance to prove the first claim - let outer_sc_inst = OuterSumcheckInstance::new( - PowPolynomial::new(&tau, num_rounds_sc).evals(), - Az.clone(), - Bz.clone(), - (0..Cz.len()) - .map(|i| U.u * Cz[i] + E[i]) - .collect::>(), - w.p.clone(), // Mz = Az + r * Bz + r^2 * Cz - &u.e, // eval_Az_at_tau + r * eval_Az_at_tau + r^2 * eval_Cz_at_tau - ); - - // a sum-check instance to prove the second claim - let val = zip_with!( - par_iter, - (pk.S_repr.val_A, pk.S_repr.val_B, pk.S_repr.val_C), - |v_a, v_b, v_c| *v_a + c * *v_b + c * c * *v_c - ) - .collect::>(); - let inner_sc_inst = InnerSumcheckInstance { - claim: eval_Az_at_tau + c * eval_Bz_at_tau + c * c * eval_Cz_at_tau, - poly_L_row: MultilinearPolynomial::new(L_row.clone()), - poly_L_col: MultilinearPolynomial::new(L_col.clone()), - poly_val: MultilinearPolynomial::new(val), - }; - - (outer_sc_inst, inner_sc_inst) - }, - || { - // a third sum-check instance to prove the read-only memory claim - // we now need to prove that L_row and L_col are well-formed - - // hash the tuples of (addr,val) memory contents and read responses into a single field element using `hash_func` - - let (comm_mem_oracles, mem_oracles, mem_aux) = - MemorySumcheckInstance::::compute_oracles( - ck, - &r, - &gamma, - &mem_row, - &pk.S_repr.row, - &L_row, - &pk.S_repr.ts_row, - &mem_col, - &pk.S_repr.col, - &L_col, - &pk.S_repr.ts_col, - )?; - // absorb the commitments - transcript.absorb(b"l", &comm_mem_oracles.as_slice()); - - let rho = transcript.squeeze(b"r")?; - let poly_eq = MultilinearPolynomial::new(PowPolynomial::new(&rho, num_rounds_sc).evals()); - - Ok::<_, NovaError>(( - MemorySumcheckInstance::new( - mem_oracles.clone(), - mem_aux, - poly_eq.Z, - pk.S_repr.ts_row.clone(), - pk.S_repr.ts_col.clone(), - ), - comm_mem_oracles, - mem_oracles, - )) - }, - ); - - let (mut mem_sc_inst, comm_mem_oracles, mem_oracles) = mem_res?; - - let mut witness_sc_inst = WitnessBoundSumcheck::new(tau, W.clone(), S.num_vars); - - let (sc, rand_sc, claims_mem, claims_outer, claims_inner, claims_witness) = Self::prove_helper( - &mut mem_sc_inst, - &mut outer_sc_inst, - &mut inner_sc_inst, - &mut witness_sc_inst, - &mut transcript, - )?; - - // claims from the end of the sum-check - let eval_Az = claims_outer[0][0]; - let eval_Bz = claims_outer[0][1]; - - let eval_L_row = claims_inner[0][0]; - let eval_L_col = claims_inner[0][1]; - - let eval_t_plus_r_inv_row = claims_mem[0][0]; - let eval_w_plus_r_inv_row = claims_mem[0][1]; - let eval_ts_row = claims_mem[0][2]; - - let eval_t_plus_r_inv_col = claims_mem[1][0]; - let eval_w_plus_r_inv_col = claims_mem[1][1]; - let eval_ts_col = claims_mem[1][2]; - let eval_W = claims_witness[0][0]; - - // compute the remaining claims that did not come for free from the sum-check prover - let (eval_Cz, eval_E, eval_val_A, eval_val_B, eval_val_C, eval_row, eval_col) = { - let e = [ - &Cz, - &E, - &pk.S_repr.val_A, - &pk.S_repr.val_B, - &pk.S_repr.val_C, - &pk.S_repr.row, - &pk.S_repr.col, - ] - .into_par_iter() - .map(|p| MultilinearPolynomial::evaluate_with(p, &rand_sc)) - .collect::>(); - (e[0], e[1], e[2], e[3], e[4], e[5], e[6]) - }; - - // all the evaluations are at rand_sc, we can fold them into one claim - let eval_vec = vec![ - eval_W, - eval_Az, - eval_Bz, - eval_Cz, - eval_E, - eval_L_row, - eval_L_col, - eval_val_A, - eval_val_B, - eval_val_C, - eval_t_plus_r_inv_row, - eval_row, - eval_w_plus_r_inv_row, - eval_ts_row, - eval_t_plus_r_inv_col, - eval_col, - eval_w_plus_r_inv_col, - eval_ts_col, - ]; - - let comm_vec = [ - U.comm_W, - comm_Az, - comm_Bz, - comm_Cz, - U.comm_E, - comm_L_row, - comm_L_col, - pk.S_comm.comm_val_A, - pk.S_comm.comm_val_B, - pk.S_comm.comm_val_C, - comm_mem_oracles[0], - pk.S_comm.comm_row, - comm_mem_oracles[1], - pk.S_comm.comm_ts_row, - comm_mem_oracles[2], - pk.S_comm.comm_col, - comm_mem_oracles[3], - pk.S_comm.comm_ts_col, - ]; - let poly_vec = [ - &W, - &Az, - &Bz, - &Cz, - &E, - &L_row, - &L_col, - &pk.S_repr.val_A, - &pk.S_repr.val_B, - &pk.S_repr.val_C, - mem_oracles[0].as_ref(), - &pk.S_repr.row, - mem_oracles[1].as_ref(), - &pk.S_repr.ts_row, - mem_oracles[2].as_ref(), - &pk.S_repr.col, - mem_oracles[3].as_ref(), - &pk.S_repr.ts_col, - ]; - transcript.absorb(b"e", &eval_vec.as_slice()); // comm_vec is already in the transcript - let c = transcript.squeeze(b"c")?; - let w: PolyEvalWitness = PolyEvalWitness::batch(&poly_vec, &c); - let u: PolyEvalInstance = PolyEvalInstance::batch(&comm_vec, rand_sc.clone(), &eval_vec, &c); - - let eval_arg = EE::prove(ck, &pk.pk_ee, &mut transcript, &u.c, &w.p, &rand_sc, &u.e)?; - - Ok(Self { - comm_Az: comm_Az.compress(), - comm_Bz: comm_Bz.compress(), - comm_Cz: comm_Cz.compress(), - comm_L_row: comm_L_row.compress(), - comm_L_col: comm_L_col.compress(), - - comm_t_plus_r_inv_row: comm_mem_oracles[0].compress(), - comm_w_plus_r_inv_row: comm_mem_oracles[1].compress(), - comm_t_plus_r_inv_col: comm_mem_oracles[2].compress(), - comm_w_plus_r_inv_col: comm_mem_oracles[3].compress(), - - eval_Az_at_tau, - eval_Bz_at_tau, - eval_Cz_at_tau, - - sc, - - eval_Az, - eval_Bz, - eval_Cz, - eval_E, - eval_L_row, - eval_L_col, - eval_val_A, - eval_val_B, - eval_val_C, - - eval_W, - - eval_t_plus_r_inv_row, - eval_row, - eval_w_plus_r_inv_row, - eval_ts_row, - - eval_col, - eval_t_plus_r_inv_col, - eval_w_plus_r_inv_col, - eval_ts_col, - - eval_arg, - }) - } - - /// verifies a proof of satisfiability of a `RelaxedR1CS` instance - fn verify(&self, vk: &Self::VerifierKey, U: &RelaxedR1CSInstance) -> Result<(), NovaError> { - let mut transcript = E::TE::new(b"RelaxedR1CSSNARK"); - - // append the verifier key (including commitment to R1CS matrices) and the RelaxedR1CSInstance to the transcript - transcript.absorb(b"vk", &vk.digest()); - transcript.absorb(b"U", U); - - let comm_Az = Commitment::::decompress(&self.comm_Az)?; - let comm_Bz = Commitment::::decompress(&self.comm_Bz)?; - let comm_Cz = Commitment::::decompress(&self.comm_Cz)?; - let comm_L_row = Commitment::::decompress(&self.comm_L_row)?; - let comm_L_col = Commitment::::decompress(&self.comm_L_col)?; - let comm_t_plus_r_inv_row = Commitment::::decompress(&self.comm_t_plus_r_inv_row)?; - let comm_w_plus_r_inv_row = Commitment::::decompress(&self.comm_w_plus_r_inv_row)?; - let comm_t_plus_r_inv_col = Commitment::::decompress(&self.comm_t_plus_r_inv_col)?; - let comm_w_plus_r_inv_col = Commitment::::decompress(&self.comm_w_plus_r_inv_col)?; - - transcript.absorb(b"c", &[comm_Az, comm_Bz, comm_Cz].as_slice()); - - let num_rounds_sc = vk.S_comm.N.log_2(); - let tau = transcript.squeeze(b"t")?; - let tau_coords = PowPolynomial::new(&tau, num_rounds_sc).coordinates(); - - // add claims about Az, Bz, and Cz to be checked later - // since all the three polynomials are opened at tau, - // we can combine them into a single polynomial opened at tau - let eval_vec = vec![ - self.eval_Az_at_tau, - self.eval_Bz_at_tau, - self.eval_Cz_at_tau, - ]; - - transcript.absorb(b"e", &eval_vec.as_slice()); - - transcript.absorb(b"e", &vec![comm_L_row, comm_L_col].as_slice()); - let comm_vec = vec![comm_Az, comm_Bz, comm_Cz]; - let c = transcript.squeeze(b"c")?; - let u: PolyEvalInstance = - PolyEvalInstance::batch(&comm_vec, tau_coords.clone(), &eval_vec, &c); - let claim = u.e; - - let gamma = transcript.squeeze(b"g")?; - - let r = transcript.squeeze(b"r")?; - - transcript.absorb( - b"l", - &vec![ - comm_t_plus_r_inv_row, - comm_w_plus_r_inv_row, - comm_t_plus_r_inv_col, - comm_w_plus_r_inv_col, - ] - .as_slice(), - ); - - let rho = transcript.squeeze(b"r")?; - - let num_claims = 10; - let s = transcript.squeeze(b"r")?; - let coeffs = powers(&s, num_claims); - let claim = (coeffs[7] + coeffs[8]) * claim; // rest are zeros - - // verify sc - let (claim_sc_final, rand_sc) = self.sc.verify(claim, num_rounds_sc, 3, &mut transcript)?; - - // verify claim_sc_final - let claim_sc_final_expected = { - let rand_eq_bound_rand_sc = PowPolynomial::new(&rho, num_rounds_sc).evaluate(&rand_sc); - let eq_tau: EqPolynomial<_> = PowPolynomial::new(&tau, num_rounds_sc).into(); - - let taus_bound_rand_sc = eq_tau.evaluate(&rand_sc); - let taus_masked_bound_rand_sc = - MaskedEqPolynomial::new(&eq_tau, vk.num_vars.log_2()).evaluate(&rand_sc); - - let eval_t_plus_r_row = { - let eval_addr_row = IdentityPolynomial::new(num_rounds_sc).evaluate(&rand_sc); - let eval_val_row = taus_bound_rand_sc; - let eval_t = eval_addr_row + gamma * eval_val_row; - eval_t + r - }; - - let eval_w_plus_r_row = { - let eval_addr_row = self.eval_row; - let eval_val_row = self.eval_L_row; - let eval_w = eval_addr_row + gamma * eval_val_row; - eval_w + r - }; - - let eval_t_plus_r_col = { - let eval_addr_col = IdentityPolynomial::new(num_rounds_sc).evaluate(&rand_sc); - - // memory contents is z, so we compute eval_Z from eval_W and eval_X - let eval_val_col = { - // rand_sc was padded, so we now remove the padding - let (factor, rand_sc_unpad) = { - let l = vk.S_comm.N.log_2() - (2 * vk.num_vars).log_2(); - - let mut factor = E::Scalar::ONE; - for r_p in rand_sc.iter().take(l) { - factor *= E::Scalar::ONE - r_p - } - - let rand_sc_unpad = rand_sc[l..].to_vec(); - - (factor, rand_sc_unpad) - }; - - let eval_X = { - // constant term - let poly_X = std::iter::once((0, U.u)) - .chain( - //remaining inputs - (0..U.X.len()) - // filter_map uses the sparsity of the polynomial, if irrelevant - // we should replace by UniPoly - .filter_map(|i| (!U.X[i].is_zero_vartime()).then_some((i + 1, U.X[i]))), - ) - .collect(); - SparsePolynomial::new(vk.num_vars.log_2(), poly_X).evaluate(&rand_sc_unpad[1..]) - }; - - self.eval_W + factor * rand_sc_unpad[0] * eval_X - }; - let eval_t = eval_addr_col + gamma * eval_val_col; - eval_t + r - }; - - let eval_w_plus_r_col = { - let eval_addr_col = self.eval_col; - let eval_val_col = self.eval_L_col; - let eval_w = eval_addr_col + gamma * eval_val_col; - eval_w + r - }; - - let claim_mem_final_expected: E::Scalar = coeffs[0] - * (self.eval_t_plus_r_inv_row - self.eval_w_plus_r_inv_row) - + coeffs[1] * (self.eval_t_plus_r_inv_col - self.eval_w_plus_r_inv_col) - + coeffs[2] - * (rand_eq_bound_rand_sc - * (self.eval_t_plus_r_inv_row * eval_t_plus_r_row - self.eval_ts_row)) - + coeffs[3] - * (rand_eq_bound_rand_sc - * (self.eval_w_plus_r_inv_row * eval_w_plus_r_row - E::Scalar::ONE)) - + coeffs[4] - * (rand_eq_bound_rand_sc - * (self.eval_t_plus_r_inv_col * eval_t_plus_r_col - self.eval_ts_col)) - + coeffs[5] - * (rand_eq_bound_rand_sc - * (self.eval_w_plus_r_inv_col * eval_w_plus_r_col - E::Scalar::ONE)); - - let claim_outer_final_expected = coeffs[6] - * taus_bound_rand_sc - * (self.eval_Az * self.eval_Bz - U.u * self.eval_Cz - self.eval_E) - + coeffs[7] * taus_bound_rand_sc * (self.eval_Az + c * self.eval_Bz + c * c * self.eval_Cz); - let claim_inner_final_expected = coeffs[8] - * self.eval_L_row - * self.eval_L_col - * (self.eval_val_A + c * self.eval_val_B + c * c * self.eval_val_C); - - let claim_witness_final_expected = coeffs[9] * taus_masked_bound_rand_sc * self.eval_W; - - claim_mem_final_expected - + claim_outer_final_expected - + claim_inner_final_expected - + claim_witness_final_expected - }; - - if claim_sc_final_expected != claim_sc_final { - return Err(NovaError::InvalidSumcheckProof); - } - - let eval_vec = vec![ - self.eval_W, - self.eval_Az, - self.eval_Bz, - self.eval_Cz, - self.eval_E, - self.eval_L_row, - self.eval_L_col, - self.eval_val_A, - self.eval_val_B, - self.eval_val_C, - self.eval_t_plus_r_inv_row, - self.eval_row, - self.eval_w_plus_r_inv_row, - self.eval_ts_row, - self.eval_t_plus_r_inv_col, - self.eval_col, - self.eval_w_plus_r_inv_col, - self.eval_ts_col, - ]; - - let comm_vec = [ - U.comm_W, - comm_Az, - comm_Bz, - comm_Cz, - U.comm_E, - comm_L_row, - comm_L_col, - vk.S_comm.comm_val_A, - vk.S_comm.comm_val_B, - vk.S_comm.comm_val_C, - comm_t_plus_r_inv_row, - vk.S_comm.comm_row, - comm_w_plus_r_inv_row, - vk.S_comm.comm_ts_row, - comm_t_plus_r_inv_col, - vk.S_comm.comm_col, - comm_w_plus_r_inv_col, - vk.S_comm.comm_ts_col, - ]; - transcript.absorb(b"e", &eval_vec.as_slice()); // comm_vec is already in the transcript - let c = transcript.squeeze(b"c")?; - let u: PolyEvalInstance = PolyEvalInstance::batch(&comm_vec, rand_sc.clone(), &eval_vec, &c); - - // verify - EE::verify( - &vk.vk_ee, - &mut transcript, - &u.c, - &rand_sc, - &u.e, - &self.eval_arg, - )?; - - Ok(()) - } -} -#[cfg(test)] -mod tests { - use crate::provider::PallasEngine; - - use super::*; - use ff::Field; - use pasta_curves::Fq as Scalar; - - #[test] - fn test_padded() { - let mut rng = rand::thread_rng(); - let e = Scalar::random(&mut rng); - let v: Vec = (0..10).map(|_| Scalar::random(&mut rng)).collect(); - let n = 20; - - let result = padded::(&v, n, &e); - - assert_eq!(result.len(), n); - assert_eq!(&result[..10], &v[..]); - assert!(result[10..].iter().all(|&i| i == e)); - } -} +pub type RelaxedR1CSSNARK = crate::spartan::batched_ppsnark::BatchedRelaxedR1CSSNARK; diff --git a/src/spartan/snark.rs b/src/spartan/snark.rs index 3b82c3e57..21f3eeddb 100644 --- a/src/spartan/snark.rs +++ b/src/spartan/snark.rs @@ -5,415 +5,27 @@ //! an IPA-based polynomial commitment scheme. use crate::{ - digest::{DigestComputer, SimpleDigestible}, errors::NovaError, - r1cs::{R1CSShape, RelaxedR1CSInstance, RelaxedR1CSWitness, SparseMatrix}, spartan::{ - compute_eval_table_sparse, - polys::{ - eq::EqPolynomial, - multilinear::{MultilinearPolynomial, SparsePolynomial}, - power::PowPolynomial, - }, + polys::{eq::EqPolynomial, multilinear::MultilinearPolynomial}, powers, sumcheck::SumcheckProof, PolyEvalInstance, PolyEvalWitness, }, - traits::{ - evaluation::EvaluationEngineTrait, - snark::{DigestHelperTrait, RelaxedR1CSSNARKTrait}, - Engine, TranscriptEngineTrait, - }, - CommitmentKey, + traits::{Engine, TranscriptEngineTrait}, }; - -use ff::Field; use itertools::Itertools as _; -use once_cell::sync::OnceCell; -use rayon::prelude::*; -use serde::{Deserialize, Serialize}; -use std::{iter, sync::Arc}; /// A type that represents the prover's key -#[derive(Debug, Clone)] -pub struct ProverKey> { - pk_ee: EE::ProverKey, - vk_digest: E::Scalar, // digest of the verifier's key -} +pub type ProverKey = crate::spartan::batched::ProverKey; /// A type that represents the verifier's key -#[derive(Debug, Clone, Serialize)] -#[serde(bound = "")] -pub struct VerifierKey> { - vk_ee: EE::VerifierKey, - S: R1CSShape, - #[serde(skip, default = "OnceCell::new")] - digest: OnceCell, -} - -impl> SimpleDigestible for VerifierKey {} - -impl> VerifierKey { - fn new(shape: R1CSShape, vk_ee: EE::VerifierKey) -> Self { - Self { - vk_ee, - S: shape, - digest: OnceCell::new(), - } - } -} - -impl> DigestHelperTrait for VerifierKey { - /// Returns the digest of the verifier's key. - fn digest(&self) -> E::Scalar { - self - .digest - .get_or_try_init(|| { - let dc = DigestComputer::::new(self); - dc.digest() - }) - .cloned() - .expect("Failure to retrieve digest!") - } -} +pub type VerifierKey = crate::spartan::batched::VerifierKey; /// A succinct proof of knowledge of a witness to a relaxed R1CS instance /// The proof is produced using Spartan's combination of the sum-check and /// the commitment to a vector viewed as a polynomial commitment -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(bound = "")] -pub struct RelaxedR1CSSNARK> { - sc_proof_outer: SumcheckProof, - claims_outer: (E::Scalar, E::Scalar, E::Scalar), - eval_E: E::Scalar, - sc_proof_inner: SumcheckProof, - eval_W: E::Scalar, - sc_proof_batch: SumcheckProof, - evals_batch: Vec, - eval_arg: EE::EvaluationArgument, -} - -impl> RelaxedR1CSSNARKTrait for RelaxedR1CSSNARK { - type ProverKey = ProverKey; - type VerifierKey = VerifierKey; - - fn setup( - ck: Arc>, - S: &R1CSShape, - ) -> Result<(Self::ProverKey, Self::VerifierKey), NovaError> { - let (pk_ee, vk_ee) = EE::setup(ck); - - let S = S.pad(); - - let vk: VerifierKey = VerifierKey::new(S, vk_ee); - - let pk = ProverKey { - pk_ee, - vk_digest: vk.digest(), - }; - - Ok((pk, vk)) - } - - /// produces a succinct proof of satisfiability of a `RelaxedR1CS` instance - #[tracing::instrument(skip_all, name = "SNARK::prove")] - fn prove( - ck: &CommitmentKey, - pk: &Self::ProverKey, - S: &R1CSShape, - U: &RelaxedR1CSInstance, - W: &RelaxedR1CSWitness, - ) -> Result { - // pad the R1CSShape - let S = S.pad(); - // sanity check that R1CSShape has all required size characteristics - assert!(S.is_regular_shape()); - - let W = W.pad(&S); // pad the witness - let mut transcript = E::TE::new(b"RelaxedR1CSSNARK"); - - // append the digest of vk (which includes R1CS matrices) and the RelaxedR1CSInstance to the transcript - transcript.absorb(b"vk", &pk.vk_digest); - transcript.absorb(b"U", U); - - // compute the full satisfying assignment by concatenating W.W, U.u, and U.X - let mut z = [W.W.clone(), vec![U.u], U.X.clone()].concat(); - - let (num_rounds_x, num_rounds_y) = ( - usize::try_from(S.num_cons.ilog2()).unwrap(), - (usize::try_from(S.num_vars.ilog2()).unwrap() + 1), - ); - - // outer sum-check - let tau: EqPolynomial<_> = PowPolynomial::new(&transcript.squeeze(b"t")?, num_rounds_x).into(); - - let mut poly_tau = MultilinearPolynomial::new(tau.evals()); - let (mut poly_Az, mut poly_Bz, poly_Cz, mut poly_uCz_E) = { - let (poly_Az, poly_Bz, poly_Cz) = S.multiply_vec(&z)?; - let poly_uCz_E = (0..S.num_cons) - .into_par_iter() - .map(|i| U.u * poly_Cz[i] + W.E[i]) - .collect::>(); - ( - MultilinearPolynomial::new(poly_Az), - MultilinearPolynomial::new(poly_Bz), - MultilinearPolynomial::new(poly_Cz), - MultilinearPolynomial::new(poly_uCz_E), - ) - }; - - let comb_func_outer = - |poly_A_comp: &E::Scalar, - poly_B_comp: &E::Scalar, - poly_C_comp: &E::Scalar, - poly_D_comp: &E::Scalar| - -> E::Scalar { *poly_A_comp * (*poly_B_comp * *poly_C_comp - *poly_D_comp) }; - let (sc_proof_outer, r_x, claims_outer) = SumcheckProof::prove_cubic_with_additive_term( - &E::Scalar::ZERO, // claim is zero - num_rounds_x, - &mut poly_tau, - &mut poly_Az, - &mut poly_Bz, - &mut poly_uCz_E, - comb_func_outer, - &mut transcript, - )?; - - // claims from the end of sum-check - let (claim_Az, claim_Bz): (E::Scalar, E::Scalar) = (claims_outer[1], claims_outer[2]); - let chis_r_x = EqPolynomial::evals_from_points(&r_x); - - let claim_Cz = MultilinearPolynomial::evaluate_with_chis(poly_Cz.evaluations(), &chis_r_x); - let eval_E = MultilinearPolynomial::evaluate_with_chis(&W.E, &chis_r_x); - transcript.absorb( - b"claims_outer", - &[claim_Az, claim_Bz, claim_Cz, eval_E].as_slice(), - ); - - // inner sum-check - let r = transcript.squeeze(b"r")?; - let claim_inner_joint = claim_Az + r * claim_Bz + r * r * claim_Cz; - - let poly_ABC = { - // compute the initial evaluation table for R(\tau, x) - let evals_rx = EqPolynomial::evals_from_points(&r_x.clone()); - - let (evals_A, evals_B, evals_C) = compute_eval_table_sparse(&S, &evals_rx); - - assert_eq!(evals_A.len(), evals_B.len()); - assert_eq!(evals_A.len(), evals_C.len()); - (0..evals_A.len()) - .into_par_iter() - .map(|i| evals_A[i] + r * evals_B[i] + r * r * evals_C[i]) - .collect::>() - }; - - let poly_z = { - z.resize(S.num_vars * 2, E::Scalar::ZERO); - z - }; - - let comb_func = |poly_A_comp: &E::Scalar, poly_B_comp: &E::Scalar| -> E::Scalar { - *poly_A_comp * *poly_B_comp - }; - let (sc_proof_inner, r_y, _claims_inner) = SumcheckProof::prove_quad( - &claim_inner_joint, - num_rounds_y, - &mut MultilinearPolynomial::new(poly_ABC), - &mut MultilinearPolynomial::new(poly_z), - comb_func, - &mut transcript, - )?; - - // Add additional claims about W and E polynomials to the list from CC - // We will reduce a vector of claims of evaluations at different points into claims about them at the same point. - // For example, eval_W =? W(r_y[1..]) and eval_E =? E(r_x) into - // two claims: eval_W_prime =? W(rz) and eval_E_prime =? E(rz) - // We can them combine the two into one: eval_W_prime + gamma * eval_E_prime =? (W + gamma*E)(rz), - // where gamma is a public challenge - // Since commitments to W and E are homomorphic, the verifier can compute a commitment - // to the batched polynomial. - let eval_W = MultilinearPolynomial::evaluate_with(&W.W, &r_y[1..]); - - let w_vec = vec![PolyEvalWitness { p: W.W }, PolyEvalWitness { p: W.E }]; - let u_vec = vec![ - PolyEvalInstance { - c: U.comm_W, - x: r_y[1..].to_vec(), - e: eval_W, - }, - PolyEvalInstance { - c: U.comm_E, - x: r_x, - e: eval_E, - }, - ]; - - let (batched_u, batched_w, sc_proof_batch, claims_batch_left) = - batch_eval_prove(u_vec, &w_vec, &mut transcript)?; - - let eval_arg = EE::prove( - ck, - &pk.pk_ee, - &mut transcript, - &batched_u.c, - &batched_w.p, - &batched_u.x, - &batched_u.e, - )?; - - Ok(Self { - sc_proof_outer, - claims_outer: (claim_Az, claim_Bz, claim_Cz), - eval_E, - sc_proof_inner, - eval_W, - sc_proof_batch, - evals_batch: claims_batch_left, - eval_arg, - }) - } - - /// verifies a proof of satisfiability of a `RelaxedR1CS` instance - fn verify(&self, vk: &Self::VerifierKey, U: &RelaxedR1CSInstance) -> Result<(), NovaError> { - let mut transcript = E::TE::new(b"RelaxedR1CSSNARK"); - - // append the digest of R1CS matrices and the RelaxedR1CSInstance to the transcript - transcript.absorb(b"vk", &vk.digest()); - transcript.absorb(b"U", U); - - let (num_rounds_x, num_rounds_y) = ( - usize::try_from(vk.S.num_cons.ilog2()).unwrap(), - (usize::try_from(vk.S.num_vars.ilog2()).unwrap() + 1), - ); - - // outer sum-check - let tau: EqPolynomial<_> = PowPolynomial::new(&transcript.squeeze(b"t")?, num_rounds_x).into(); - - let (claim_outer_final, r_x) = - self - .sc_proof_outer - .verify(E::Scalar::ZERO, num_rounds_x, 3, &mut transcript)?; - - // verify claim_outer_final - let (claim_Az, claim_Bz, claim_Cz) = self.claims_outer; - let taus_bound_rx = tau.evaluate(&r_x); - let claim_outer_final_expected = - taus_bound_rx * (claim_Az * claim_Bz - U.u * claim_Cz - self.eval_E); - if claim_outer_final != claim_outer_final_expected { - return Err(NovaError::InvalidSumcheckProof); - } - - transcript.absorb( - b"claims_outer", - &[ - self.claims_outer.0, - self.claims_outer.1, - self.claims_outer.2, - self.eval_E, - ] - .as_slice(), - ); - - // inner sum-check - let r = transcript.squeeze(b"r")?; - let claim_inner_joint = - self.claims_outer.0 + r * self.claims_outer.1 + r * r * self.claims_outer.2; - - let (claim_inner_final, r_y) = - self - .sc_proof_inner - .verify(claim_inner_joint, num_rounds_y, 2, &mut transcript)?; - - // verify claim_inner_final - let eval_Z = { - let eval_X = { - // constant term - let poly_X = iter::once((0, U.u)) - .chain( - //remaining inputs - (0..U.X.len()) - // filter_map uses the sparsity of the polynomial, if irrelevant - // we should replace by UniPoly - .filter_map(|i| (!U.X[i].is_zero_vartime()).then_some((i + 1, U.X[i]))), - ) - .collect(); - SparsePolynomial::new(usize::try_from(vk.S.num_vars.ilog2()).unwrap(), poly_X) - .evaluate(&r_y[1..]) - }; - (E::Scalar::ONE - r_y[0]) * self.eval_W + r_y[0] * eval_X - }; - - // compute evaluations of R1CS matrices - let multi_evaluate = |M_vec: &[&SparseMatrix], - r_x: &[E::Scalar], - r_y: &[E::Scalar]| - -> Vec { - let evaluate_with_table = - |M: &SparseMatrix, T_x: &[E::Scalar], T_y: &[E::Scalar]| -> E::Scalar { - M.indptr - .par_windows(2) - .enumerate() - .map(|(row_idx, ptrs)| { - M.get_row_unchecked(ptrs.try_into().unwrap()) - .map(|(val, col_idx)| T_x[row_idx] * T_y[*col_idx] * val) - .sum::() - }) - .sum() - }; - - let (T_x, T_y) = rayon::join( - || EqPolynomial::evals_from_points(r_x), - || EqPolynomial::evals_from_points(r_y), - ); - - (0..M_vec.len()) - .into_par_iter() - .map(|i| evaluate_with_table(M_vec[i], &T_x, &T_y)) - .collect() - }; - - let evals = multi_evaluate(&[&vk.S.A, &vk.S.B, &vk.S.C], &r_x, &r_y); - - let claim_inner_final_expected = (evals[0] + r * evals[1] + r * r * evals[2]) * eval_Z; - if claim_inner_final != claim_inner_final_expected { - return Err(NovaError::InvalidSumcheckProof); - } - - // add claims about W and E polynomials - let u_vec: Vec> = vec![ - PolyEvalInstance { - c: U.comm_W, - x: r_y[1..].to_vec(), - e: self.eval_W, - }, - PolyEvalInstance { - c: U.comm_E, - x: r_x, - e: self.eval_E, - }, - ]; - - let batched_u = batch_eval_verify( - u_vec, - &mut transcript, - &self.sc_proof_batch, - &self.evals_batch, - )?; - - // verify - EE::verify( - &vk.vk_ee, - &mut transcript, - &batched_u.c, - &batched_u.x, - &batched_u.e, - &self.eval_arg, - )?; - - Ok(()) - } -} +pub type RelaxedR1CSSNARK = crate::spartan::batched::BatchedRelaxedR1CSSNARK; /// Proves a batch of polynomial evaluation claims using Sumcheck /// reducing them to a single claim at the same point. diff --git a/src/spartan/sumcheck/mod.rs b/src/spartan/sumcheck/mod.rs index 4a59b175d..19ab32119 100644 --- a/src/spartan/sumcheck/mod.rs +++ b/src/spartan/sumcheck/mod.rs @@ -126,56 +126,6 @@ impl SumcheckProof { ) } - pub fn prove_quad( - claim: &E::Scalar, - num_rounds: usize, - poly_A: &mut MultilinearPolynomial, - poly_B: &mut MultilinearPolynomial, - comb_func: F, - transcript: &mut E::TE, - ) -> Result<(Self, Vec, Vec), NovaError> - where - F: Fn(&E::Scalar, &E::Scalar) -> E::Scalar + Sync, - { - let mut r: Vec = Vec::new(); - let mut polys: Vec> = Vec::new(); - let mut claim_per_round = *claim; - for _ in 0..num_rounds { - let poly = { - let (eval_point_0, eval_point_2) = - Self::compute_eval_points_quad(poly_A, poly_B, &comb_func); - - let evals = vec![eval_point_0, claim_per_round - eval_point_0, eval_point_2]; - UniPoly::from_evals(&evals) - }; - - // append the prover's message to the transcript - transcript.absorb(b"p", &poly); - - //derive the verifier's challenge for the next round - let r_i = transcript.squeeze(b"c")?; - r.push(r_i); - polys.push(poly.compress()); - - // Set up next round - claim_per_round = poly.evaluate(&r_i); - - // bind all tables to the verifier's challenge - rayon::join( - || poly_A.bind_poly_var_top(&r_i), - || poly_B.bind_poly_var_top(&r_i), - ); - } - - Ok(( - Self { - compressed_polys: polys, - }, - r, - vec![poly_A[0], poly_B[0]], - )) - } - pub fn prove_quad_batch( claims: &[E::Scalar], num_rounds: &[usize], @@ -401,77 +351,6 @@ impl SumcheckProof { ) } - pub fn prove_cubic_with_additive_term( - claim: &E::Scalar, - num_rounds: usize, - poly_A: &mut MultilinearPolynomial, - poly_B: &mut MultilinearPolynomial, - poly_C: &mut MultilinearPolynomial, - poly_D: &mut MultilinearPolynomial, - comb_func: F, - transcript: &mut E::TE, - ) -> Result<(Self, Vec, Vec), NovaError> - where - F: Fn(&E::Scalar, &E::Scalar, &E::Scalar, &E::Scalar) -> E::Scalar + Sync, - { - let mut r: Vec = Vec::new(); - let mut polys: Vec> = Vec::new(); - let mut claim_per_round = *claim; - - for _ in 0..num_rounds { - let poly = { - // Make an iterator returning the contributions to the evaluations - let (eval_point_0, eval_point_2, eval_point_3) = - Self::compute_eval_points_cubic_with_additive_term( - poly_A, poly_B, poly_C, poly_D, &comb_func, - ); - - let evals = vec![ - eval_point_0, - claim_per_round - eval_point_0, - eval_point_2, - eval_point_3, - ]; - UniPoly::from_evals(&evals) - }; - - // append the prover's message to the transcript - transcript.absorb(b"p", &poly); - - //derive the verifier's challenge for the next round - let r_i = transcript.squeeze(b"c")?; - r.push(r_i); - polys.push(poly.compress()); - - // Set up next round - claim_per_round = poly.evaluate(&r_i); - - // bound all tables to the verifier's challenge - rayon::join( - || { - rayon::join( - || poly_A.bind_poly_var_top(&r_i), - || poly_B.bind_poly_var_top(&r_i), - ) - }, - || { - rayon::join( - || poly_C.bind_poly_var_top(&r_i), - || poly_D.bind_poly_var_top(&r_i), - ) - }, - ); - } - - Ok(( - Self { - compressed_polys: polys, - }, - r, - vec![poly_A[0], poly_B[0], poly_C[0], poly_D[0]], - )) - } - pub fn prove_cubic_with_additive_term_batch( claims: &[E::Scalar], num_rounds: &[usize],