Skip to content

Commit

Permalink
fix: remove a duplicate R1CSShape in the RelaxedR1CSSNARK's chose…
Browse files Browse the repository at this point in the history
…n `ProverKey` (#66) (#237)

* fix: remove a duplicate `R1CSShape` in the `RelaxedR1CSSNARK`'s chosen `ProverKey`

As explained by @zaverucha in microsoft/Spartan2#2 (with edits for precision):

The `R1CSShape` object was being stored in both:
- the `ProverKey` of the `spartan::direct::DirectSNARK`, which generically employs any instance of `RelaxedR1CSSNARKTrait`,
- the `ProverKey` of each of the two `RelaxedR1CSSNARKTrait` implementations in `spartan::{snark, ppsnark}::RelaxedR1CSSNARK`,
- the `PublicParams` that are passed to the `crate::CompressedSNARK<G1, G2, C1, C2, S1, S2>`, which generically employs any instance of `RelaxedR1CSSNARKTrait`.

IOW, `RelaxedR1CSSNARKTrait` is always accessed through generic structs (`DirectSNARK`, `CompressedSNARK`) which already have a copy of the relevant R1CSSHape. Westore it once in the top level object (RelaxedR1CSSNARKTrait) and pass it to the Spartan implementation.

This saves memory and makes serialization of the ProverKey about twice as fast. Both are significant when there are a large number of constraints.

* refactor: rename check_regular_shape into is_regular_shape
  • Loading branch information
huitseeker authored Oct 27, 2023
1 parent e79498b commit e32c64d
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 30 deletions.
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,7 @@ where
S1::prove(
&pp.ck_primary,
&pk.pk_primary,
&pp.r1cs_shape_primary,
&recursive_snark.r_U_primary,
&recursive_snark.r_W_primary,
)
Expand All @@ -690,6 +691,7 @@ where
S2::prove(
&pp.ck_secondary,
&pk.pk_secondary,
&pp.r1cs_shape_secondary,
&f_U_secondary,
&f_W_secondary,
)
Expand Down
15 changes: 8 additions & 7 deletions src/r1cs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,14 @@ impl<G: Group> R1CSShape<G> {
}

// Checks regularity conditions on the R1CSShape, required in Spartan-class SNARKs
// Panics if num_cons, num_vars, or num_io are not powers of two, or if num_io > num_vars
// Returns false if num_cons, num_vars, or num_io are not powers of two, or if num_io > num_vars
#[inline]
pub(crate) fn check_regular_shape(&self) {
assert_eq!(self.num_cons.next_power_of_two(), self.num_cons);
assert_eq!(self.num_vars.next_power_of_two(), self.num_vars);
assert_eq!(self.num_io.next_power_of_two(), self.num_io);
assert!(self.num_io < self.num_vars);
pub(crate) fn is_regular_shape(&self) -> bool {
let cons_valid = self.num_cons.next_power_of_two() == self.num_cons;
let vars_valid = self.num_vars.next_power_of_two() == self.num_vars;
let io_valid = self.num_io.next_power_of_two() == self.num_io;
let io_lt_vars = self.num_io < self.num_vars;
cons_valid && vars_valid && io_valid && io_lt_vars
}

pub fn multiply_vec(
Expand Down Expand Up @@ -637,7 +638,7 @@ mod tests {

fn test_pad_tiny_r1cs_with<G: Group>() {
let padded_r1cs = tiny_r1cs::<G>(3).pad();
padded_r1cs.check_regular_shape();
assert!(padded_r1cs.is_regular_shape());

let expected_r1cs = tiny_r1cs::<G>(4);

Expand Down
2 changes: 1 addition & 1 deletion src/spartan/direct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ impl<G: Group, S: RelaxedR1CSSNARKTrait<G>, C: StepCircuit<G::Scalar>> DirectSNA
);

// prove the instance using Spartan
let snark = S::prove(&pk.ck, &pk.pk, &u_relaxed, &w_relaxed)?;
let snark = S::prove(&pk.ck, &pk.pk, &pk.S, &u_relaxed, &w_relaxed)?;

Ok(DirectSNARK {
comm_W: u.comm_W,
Expand Down
19 changes: 10 additions & 9 deletions src/spartan/ppsnark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,6 @@ impl<G: Group> SumcheckEngine<G> for InnerSumcheckInstance<G> {
#[serde(bound = "")]
pub struct ProverKey<G: Group, EE: EvaluationEngineTrait<G>> {
pk_ee: EE::ProverKey,
S: R1CSShape<G>,
S_repr: R1CSShapeSparkRepr<G>,
S_comm: R1CSShapeSparkCommitment<G>,
vk_digest: G::Scalar, // digest of verifier's key
Expand Down Expand Up @@ -891,7 +890,6 @@ impl<G: Group, EE: EvaluationEngineTrait<G>> RelaxedR1CSSNARKTrait<G> for Relaxe

let pk = ProverKey {
pk_ee,
S,
S_repr,
S_comm,
vk_digest: vk.digest(),
Expand All @@ -904,18 +902,21 @@ impl<G: Group, EE: EvaluationEngineTrait<G>> RelaxedR1CSSNARKTrait<G> for Relaxe
fn prove(
ck: &CommitmentKey<G>,
pk: &Self::ProverKey,
S: &R1CSShape<G>,
U: &RelaxedR1CSInstance<G>,
W: &RelaxedR1CSWitness<G>,
) -> Result<Self, NovaError> {
let W = W.pad(&pk.S); // pad the witness
// 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 = G::TE::new(b"RelaxedR1CSSNARK");

// a list of polynomial evaluation claims that will be batched
let mut w_u_vec = Vec::new();

// sanity check that R1CSShape has certain size characteristics
pk.S.check_regular_shape();

// 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);
Expand All @@ -924,7 +925,7 @@ impl<G: Group, EE: EvaluationEngineTrait<G>> RelaxedR1CSSNARKTrait<G> for Relaxe
let z = [W.W.clone(), vec![U.u], U.X.clone()].concat();

// compute Az, Bz, Cz
let (mut Az, mut Bz, mut Cz) = pk.S.multiply_vec(&z)?;
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(
Expand Down Expand Up @@ -967,7 +968,7 @@ impl<G: Group, EE: EvaluationEngineTrait<G>> RelaxedR1CSSNARKTrait<G> for Relaxe
// (2) send commitments to the following two oracles
// E_row(i) = eq(tau, row(i)) for all i
// E_col(i) = z(col(i)) for all i
let (mem_row, mem_col, E_row, E_col) = pk.S_repr.evaluation_oracles(&pk.S, &tau, &z);
let (mem_row, mem_col, E_row, E_col) = pk.S_repr.evaluation_oracles(&S, &tau, &z);
let (comm_E_row, comm_E_col) =
rayon::join(|| G::CE::commit(ck, &E_row), || G::CE::commit(ck, &E_col));

Expand Down Expand Up @@ -1281,7 +1282,7 @@ impl<G: Group, EE: EvaluationEngineTrait<G>> RelaxedR1CSSNARKTrait<G> for Relaxe
// we need to prove that eval_z = z(r_prod) = (1-r_prod[0]) * W.w(r_prod[1..]) + r_prod[0] * U.x(r_prod[1..]).
// r_prod was padded, so we now remove the padding
let r_prod_unpad = {
let l = pk.S_repr.N.log_2() - (2 * pk.S.num_vars).log_2();
let l = pk.S_repr.N.log_2() - (2 * S.num_vars).log_2();
r_prod[l..].to_vec()
};

Expand Down
27 changes: 14 additions & 13 deletions src/spartan/snark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ use serde::{Deserialize, Serialize};
#[serde(bound = "")]
pub struct ProverKey<G: Group, EE: EvaluationEngineTrait<G>> {
pk_ee: EE::ProverKey,
S: R1CSShape<G>,
vk_digest: G::Scalar, // digest of the verifier's key
}

Expand Down Expand Up @@ -96,11 +95,10 @@ impl<G: Group, EE: EvaluationEngineTrait<G>> RelaxedR1CSSNARKTrait<G> for Relaxe

let S = S.pad();

let vk: VerifierKey<G, EE> = VerifierKey::new(S.clone(), vk_ee);
let vk: VerifierKey<G, EE> = VerifierKey::new(S, vk_ee);

let pk = ProverKey {
pk_ee,
S,
vk_digest: vk.digest(),
};

Expand All @@ -111,14 +109,17 @@ impl<G: Group, EE: EvaluationEngineTrait<G>> RelaxedR1CSSNARKTrait<G> for Relaxe
fn prove(
ck: &CommitmentKey<G>,
pk: &Self::ProverKey,
S: &R1CSShape<G>,
U: &RelaxedR1CSInstance<G>,
W: &RelaxedR1CSWitness<G>,
) -> Result<Self, NovaError> {
let W = W.pad(&pk.S); // pad the witness
let mut transcript = G::TE::new(b"RelaxedR1CSSNARK");
// pad the R1CSShape
let S = S.pad();
// sanity check that R1CSShape has all required size characteristics
assert!(S.is_regular_shape());

// sanity check that R1CSShape has certain size characteristics
pk.S.check_regular_shape();
let W = W.pad(&S); // pad the witness
let mut transcript = G::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);
Expand All @@ -128,8 +129,8 @@ impl<G: Group, EE: EvaluationEngineTrait<G>> RelaxedR1CSSNARKTrait<G> for Relaxe
let mut z = [W.W.clone(), vec![U.u], U.X.clone()].concat();

let (num_rounds_x, num_rounds_y) = (
usize::try_from(pk.S.num_cons.ilog2()).unwrap(),
(usize::try_from(pk.S.num_vars.ilog2()).unwrap() + 1),
usize::try_from(S.num_cons.ilog2()).unwrap(),
(usize::try_from(S.num_vars.ilog2()).unwrap() + 1),
);

// outer sum-check
Expand All @@ -139,8 +140,8 @@ impl<G: Group, EE: EvaluationEngineTrait<G>> RelaxedR1CSSNARKTrait<G> for Relaxe

let mut poly_tau = MultilinearPolynomial::new(EqPolynomial::new(tau).evals());
let (mut poly_Az, mut poly_Bz, poly_Cz, mut poly_uCz_E) = {
let (poly_Az, poly_Bz, poly_Cz) = pk.S.multiply_vec(&z)?;
let poly_uCz_E = (0..pk.S.num_cons)
let (poly_Az, poly_Bz, poly_Cz) = S.multiply_vec(&z)?;
let poly_uCz_E = (0..S.num_cons)
.map(|i| U.u * poly_Cz[i] + W.E[i])
.collect::<Vec<G::Scalar>>();
(
Expand Down Expand Up @@ -223,7 +224,7 @@ impl<G: Group, EE: EvaluationEngineTrait<G>> RelaxedR1CSSNARKTrait<G> for Relaxe
(A_evals, B_evals, C_evals)
};

let (evals_A, evals_B, evals_C) = compute_eval_table_sparse(&pk.S, &evals_rx);
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());
Expand All @@ -234,7 +235,7 @@ impl<G: Group, EE: EvaluationEngineTrait<G>> RelaxedR1CSSNARKTrait<G> for Relaxe
};

let poly_z = {
z.resize(pk.S.num_vars * 2, G::Scalar::ZERO);
z.resize(S.num_vars * 2, G::Scalar::ZERO);
z
};

Expand Down
1 change: 1 addition & 0 deletions src/traits/snark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub trait RelaxedR1CSSNARKTrait<G: Group>:
fn prove(
ck: &CommitmentKey<G>,
pk: &Self::ProverKey,
S: &R1CSShape<G>,
U: &RelaxedR1CSInstance<G>,
W: &RelaxedR1CSWitness<G>,
) -> Result<Self, NovaError>;
Expand Down

0 comments on commit e32c64d

Please sign in to comment.