Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A more optimal preprocessing SNARK #158

Merged
merged 3 commits into from
Apr 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "nova-snark"
version = "0.19.1"
version = "0.20.0"
authors = ["Srinath Setty <srinath@microsoft.com>"]
edition = "2021"
description = "Recursive zkSNARKs without trusted setup"
Expand Down
6 changes: 2 additions & 4 deletions benches/compressed-snark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ type G1 = pasta_curves::pallas::Point;
type G2 = pasta_curves::vesta::Point;
type EE1 = nova_snark::provider::ipa_pc::EvaluationEngine<G1>;
type EE2 = nova_snark::provider::ipa_pc::EvaluationEngine<G2>;
type CC1 = nova_snark::spartan::spark::TrivialCompComputationEngine<G1, EE1>;
type CC2 = nova_snark::spartan::spark::TrivialCompComputationEngine<G2, EE2>;
type S1 = nova_snark::spartan::RelaxedR1CSSNARK<G1, EE1, CC1>;
type S2 = nova_snark::spartan::RelaxedR1CSSNARK<G2, EE2, CC2>;
type S1 = nova_snark::spartan::RelaxedR1CSSNARK<G1, EE1>;
type S2 = nova_snark::spartan::RelaxedR1CSSNARK<G2, EE2>;
type C1 = NonTrivialTestCircuit<<G1 as Group>::Scalar>;
type C2 = TrivialTestCircuit<<G2 as Group>::Scalar>;

Expand Down
6 changes: 2 additions & 4 deletions examples/minroot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,8 @@ fn main() {
let start = Instant::now();
type EE1 = nova_snark::provider::ipa_pc::EvaluationEngine<G1>;
type EE2 = nova_snark::provider::ipa_pc::EvaluationEngine<G2>;
type CC1 = nova_snark::spartan::spark::TrivialCompComputationEngine<G1, EE1>;
type CC2 = nova_snark::spartan::spark::TrivialCompComputationEngine<G2, EE2>;
type S1 = nova_snark::spartan::RelaxedR1CSSNARK<G1, EE1, CC1>;
type S2 = nova_snark::spartan::RelaxedR1CSSNARK<G2, EE2, CC2>;
type S1 = nova_snark::spartan::RelaxedR1CSSNARK<G1, EE1>;
type S2 = nova_snark::spartan::RelaxedR1CSSNARK<G2, EE2>;

let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &pk, &recursive_snark);
println!(
Expand Down
3 changes: 3 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,7 @@ pub enum NovaError {
/// returned when the product proof check fails
#[error("InvalidProductProof")]
InvalidProductProof,
/// returned when the consistency with public IO and assignment used fails
#[error("IncorrectWitness")]
IncorrectWitness,
}
12 changes: 4 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -788,10 +788,8 @@ mod tests {
type G2 = pasta_curves::vesta::Point;
type EE1 = provider::ipa_pc::EvaluationEngine<G1>;
type EE2 = provider::ipa_pc::EvaluationEngine<G2>;
type CC1 = spartan::spark::TrivialCompComputationEngine<G1, EE1>;
type CC2 = spartan::spark::TrivialCompComputationEngine<G2, EE2>;
type S1 = spartan::RelaxedR1CSSNARK<G1, EE1, CC1>;
type S2 = spartan::RelaxedR1CSSNARK<G2, EE2, CC2>;
type S1 = spartan::RelaxedR1CSSNARK<G1, EE1>;
type S2 = spartan::RelaxedR1CSSNARK<G2, EE2>;
use ::bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError};
use core::marker::PhantomData;
use ff::PrimeField;
Expand Down Expand Up @@ -1095,10 +1093,8 @@ mod tests {
assert_eq!(zn_secondary, vec![<G2 as Group>::Scalar::from(2460515u64)]);

// run the compressed snark with Spark compiler
type CC1Prime = spartan::spark::SparkEngine<G1>;
type CC2Prime = spartan::spark::SparkEngine<G2>;
type S1Prime = spartan::RelaxedR1CSSNARK<G1, EE1, CC1Prime>;
type S2Prime = spartan::RelaxedR1CSSNARK<G2, EE2, CC2Prime>;
type S1Prime = spartan::pp::RelaxedR1CSSNARK<G1, EE1>;
type S2Prime = spartan::pp::RelaxedR1CSSNARK<G2, EE2>;

// produce the prover and verifier keys for compressed snark
let (pk, vk) = CompressedSNARK::<_, _, _, _, S1Prime, S2Prime>::setup(&pp).unwrap();
Expand Down
4 changes: 2 additions & 2 deletions src/r1cs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ impl<G: Group> R1CS<G> {
pub fn commitment_key(S: &R1CSShape<G>) -> CommitmentKey<G> {
let num_cons = S.num_cons;
let num_vars = S.num_vars;
let num_nz = max(max(S.A.len(), S.B.len()), S.C.len());
G::CE::setup(b"ck", max(max(num_cons, num_vars), num_nz))
let total_nz = S.A.len() + S.B.len() + S.C.len();
G::CE::setup(b"ck", max(max(num_cons, num_vars), total_nz))
}
}

Expand Down
184 changes: 63 additions & 121 deletions src/spartan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
//! over the polynomial commitment and evaluation argument (i.e., a PCS)
mod math;
pub(crate) mod polynomial;
pub mod spark;
pub mod pp;
mod sumcheck;

use crate::{
errors::NovaError,
r1cs::{R1CSShape, RelaxedR1CSInstance, RelaxedR1CSWitness},
traits::{
evaluation::EvaluationEngineTrait, snark::RelaxedR1CSSNARKTrait, Group, TranscriptEngineTrait,
TranscriptReprTrait,
},
Commitment, CommitmentKey,
};
Expand All @@ -22,7 +21,6 @@ use serde::{Deserialize, Serialize};
use sumcheck::SumcheckProof;

/// A type that holds a witness to a polynomial evaluation instance
#[allow(dead_code)]
pub struct PolyEvalWitness<G: Group> {
p: Vec<G::Scalar>, // polynomial
}
Expand Down Expand Up @@ -56,7 +54,6 @@ impl<G: Group> PolyEvalWitness<G> {
}

/// A type that holds a polynomial evaluation instance
#[allow(dead_code)]
pub struct PolyEvalInstance<G: Group> {
c: Commitment<G>, // commitment to the polynomial
x: Vec<G::Scalar>, // evaluation point
Expand All @@ -80,107 +77,43 @@ impl<G: Group> PolyEvalInstance<G> {
}
}

/// A trait that defines the behavior of a computation commitment engine
pub trait CompCommitmentEngineTrait<G: Group> {
/// A type that holds opening hint
type Decommitment: Clone + Send + Sync + Serialize + for<'de> Deserialize<'de>;

/// A type that holds a commitment
type Commitment: Clone
+ Send
+ Sync
+ TranscriptReprTrait<G>
+ Serialize
+ for<'de> Deserialize<'de>;

/// A type that holds an evaluation argument
type EvaluationArgument: Send + Sync + Serialize + for<'de> Deserialize<'de>;

/// commits to R1CS matrices
fn commit(
ck: &CommitmentKey<G>,
S: &R1CSShape<G>,
) -> Result<(Self::Commitment, Self::Decommitment), NovaError>;

/// proves an evaluation of R1CS matrices viewed as polynomials
fn prove(
ck: &CommitmentKey<G>,
S: &R1CSShape<G>,
decomm: &Self::Decommitment,
comm: &Self::Commitment,
r: &(&[G::Scalar], &[G::Scalar]),
transcript: &mut G::TE,
) -> Result<
(
Self::EvaluationArgument,
Vec<(PolyEvalWitness<G>, PolyEvalInstance<G>)>,
),
NovaError,
>;

/// verifies an evaluation of R1CS matrices viewed as polynomials and returns verified evaluations
fn verify(
comm: &Self::Commitment,
r: &(&[G::Scalar], &[G::Scalar]),
arg: &Self::EvaluationArgument,
transcript: &mut G::TE,
) -> Result<(G::Scalar, G::Scalar, G::Scalar, Vec<PolyEvalInstance<G>>), NovaError>;
}

/// A type that represents the prover's key
#[derive(Serialize, Deserialize)]
#[serde(bound = "")]
pub struct ProverKey<
G: Group,
EE: EvaluationEngineTrait<G, CE = G::CE>,
CC: CompCommitmentEngineTrait<G>,
> {
pub struct ProverKey<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> {
pk_ee: EE::ProverKey,
S: R1CSShape<G>,
decomm: CC::Decommitment,
comm: CC::Commitment,
}

/// A type that represents the verifier's key
#[derive(Serialize, Deserialize)]
#[serde(bound = "")]
pub struct VerifierKey<
G: Group,
EE: EvaluationEngineTrait<G, CE = G::CE>,
CC: CompCommitmentEngineTrait<G>,
> {
num_cons: usize,
num_vars: usize,
pub struct VerifierKey<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> {
vk_ee: EE::VerifierKey,
comm: CC::Commitment,
S: R1CSShape<G>,
}

/// 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(Serialize, Deserialize)]
#[serde(bound = "")]
pub struct RelaxedR1CSSNARK<
G: Group,
EE: EvaluationEngineTrait<G, CE = G::CE>,
CC: CompCommitmentEngineTrait<G>,
> {
pub struct RelaxedR1CSSNARK<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> {
sc_proof_outer: SumcheckProof<G>,
claims_outer: (G::Scalar, G::Scalar, G::Scalar),
eval_E: G::Scalar,
sc_proof_inner: SumcheckProof<G>,
eval_W: G::Scalar,
eval_arg_cc: CC::EvaluationArgument,
sc_proof_batch: SumcheckProof<G>,
evals_batch: Vec<G::Scalar>,
eval_arg: EE::EvaluationArgument,
}

impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>, CC: CompCommitmentEngineTrait<G>>
RelaxedR1CSSNARKTrait<G> for RelaxedR1CSSNARK<G, EE, CC>
impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> RelaxedR1CSSNARKTrait<G>
for RelaxedR1CSSNARK<G, EE>
{
type ProverKey = ProverKey<G, EE, CC>;
type VerifierKey = VerifierKey<G, EE, CC>;
type ProverKey = ProverKey<G, EE>;
type VerifierKey = VerifierKey<G, EE>;

fn setup(
ck: &CommitmentKey<G>,
Expand All @@ -190,21 +123,12 @@ impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>, CC: CompCommitmentEngin

let S = S.pad();

let (comm, decomm) = CC::commit(ck, &S)?;

let vk = VerifierKey {
num_cons: S.num_cons,
num_vars: S.num_vars,
vk_ee,
comm: comm.clone(),
S: S.clone(),
};

let pk = ProverKey {
pk_ee,
S,
comm,
decomm,
};
let pk = ProverKey { pk_ee, S };

Ok((pk, vk))
}
Expand All @@ -225,8 +149,8 @@ impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>, CC: CompCommitmentEngin
assert_eq!(pk.S.num_io.next_power_of_two(), pk.S.num_io);
assert!(pk.S.num_io < pk.S.num_vars);

// append the commitment to R1CS matrices and the RelaxedR1CSInstance to the transcript
transcript.absorb(b"C", &pk.comm);
// append the digest of R1CS matrices and the RelaxedR1CSInstance to the transcript
transcript.absorb(b"S", &pk.S);
transcript.absorb(b"U", U);

// compute the full satisfying assignment by concatenating W.W, U.u, and U.X
Expand Down Expand Up @@ -353,17 +277,8 @@ impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>, CC: CompCommitmentEngin
&mut transcript,
)?;

// we now prove evaluations of R1CS matrices at (r_x, r_y)
let (eval_arg_cc, mut w_u_vec) = CC::prove(
ck,
&pk.S,
&pk.decomm,
&pk.comm,
&(&r_x, &r_y),
&mut transcript,
)?;

// add additional claims about W and E polynomials to the list from CC
let mut w_u_vec = Vec::new();
let eval_W = MultilinearPolynomial::evaluate_with(&W.W, &r_y[1..]);
w_u_vec.push((
PolyEvalWitness { p: W.W.clone() },
Expand Down Expand Up @@ -475,7 +390,6 @@ impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>, CC: CompCommitmentEngin
eval_E,
sc_proof_inner,
eval_W,
eval_arg_cc,
sc_proof_batch,
evals_batch: claims_batch_left,
eval_arg,
Expand All @@ -486,13 +400,13 @@ impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>, CC: CompCommitmentEngin
fn verify(&self, vk: &Self::VerifierKey, U: &RelaxedR1CSInstance<G>) -> Result<(), NovaError> {
let mut transcript = G::TE::new(b"RelaxedR1CSSNARK");

// append the commitment to R1CS matrices and the RelaxedR1CSInstance to the transcript
transcript.absorb(b"C", &vk.comm);
// append the digest of R1CS matrices and the RelaxedR1CSInstance to the transcript
transcript.absorb(b"S", &vk.S);
transcript.absorb(b"U", U);

let (num_rounds_x, num_rounds_y) = (
(vk.num_cons as f64).log2() as usize,
((vk.num_vars as f64).log2() as usize + 1),
(vk.S.num_cons as f64).log2() as usize,
((vk.S.num_vars as f64).log2() as usize + 1),
);

// outer sum-check
Expand Down Expand Up @@ -546,32 +460,60 @@ impl<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>, CC: CompCommitmentEngin
.map(|i| (i + 1, U.X[i]))
.collect::<Vec<(usize, G::Scalar)>>(),
);
SparsePolynomial::new((vk.num_vars as f64).log2() as usize, poly_X).evaluate(&r_y[1..])
SparsePolynomial::new((vk.S.num_vars as f64).log2() as usize, poly_X).evaluate(&r_y[1..])
};
(G::Scalar::one() - r_y[0]) * self.eval_W + r_y[0] * eval_X
};

// verify evaluation argument to retrieve evaluations of R1CS matrices
let (eval_A, eval_B, eval_C, mut u_vec) =
CC::verify(&vk.comm, &(&r_x, &r_y), &self.eval_arg_cc, &mut transcript)?;
// compute evaluations of R1CS matrices
let multi_evaluate = |M_vec: &[&[(usize, usize, G::Scalar)]],
r_x: &[G::Scalar],
r_y: &[G::Scalar]|
-> Vec<G::Scalar> {
let evaluate_with_table =
|M: &[(usize, usize, G::Scalar)], T_x: &[G::Scalar], T_y: &[G::Scalar]| -> G::Scalar {
(0..M.len())
.collect::<Vec<usize>>()
.par_iter()
.map(|&i| {
let (row, col, val) = M[i];
T_x[row] * T_y[col] * val
})
.reduce(G::Scalar::zero, |acc, x| acc + x)
};

let (T_x, T_y) = rayon::join(
|| EqPolynomial::new(r_x.to_vec()).evals(),
|| EqPolynomial::new(r_y.to_vec()).evals(),
);

(0..M_vec.len())
.collect::<Vec<usize>>()
.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 = (eval_A + r * eval_B + r * r * eval_C) * eval_Z;
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 additional claims about W and E polynomials to the list from CC
u_vec.push(PolyEvalInstance {
c: U.comm_W,
x: r_y[1..].to_vec(),
e: self.eval_W,
});

u_vec.push(PolyEvalInstance {
c: U.comm_E,
x: r_x,
e: self.eval_E,
});
// add claims about W and E polynomials
let u_vec: Vec<PolyEvalInstance<G>> = 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 u_vec_padded = PolyEvalInstance::pad(&u_vec); // pad the evaluation points

Expand Down
Loading