Skip to content

Commit

Permalink
feat: implement nova's zk layer (#127)
Browse files Browse the repository at this point in the history
* feat: zk nova layer

* chore: clippy + trigger CI

* chore: add comment for `new` (generating a zk nova ivc proof)

* chore: adding text reference to `sample`

* chore: use `debug_assert` instead of `cfg(test)`

* improve: pass `poseidon_config` by ref

Co-authored-by: Carlos Pérez <[email protected]>

* improve: pass `z_0` by ref

Co-authored-by: Carlos Pérez <[email protected]>

* improve: pass `r1cs` and `cf_r1cs` by ref

Co-authored-by: Carlos Pérez <[email protected]>

* chore: appropriate docs (2)

* chore: pass by ref modifications

* improve: use single sponge

* fix: remove blinding the cyclefold instance, add verifier checks on the
prover provided cyclefold intance

* fix: assert that the sampled relaxed r1cs is correct

* fix: check length of `u_i.x`

---------

Co-authored-by: Carlos Pérez <[email protected]>
  • Loading branch information
dmpierre and CPerezz authored Aug 17, 2024
1 parent 5ec9c2c commit c09c52f
Show file tree
Hide file tree
Showing 4 changed files with 537 additions and 9 deletions.
104 changes: 101 additions & 3 deletions folding-schemes/src/arith/r1cs.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
use crate::commitment::CommitmentScheme;
use crate::folding::nova::{CommittedInstance, Witness};
use crate::RngCore;
use ark_crypto_primitives::sponge::Absorb;
use ark_ec::{CurveGroup, Group};
use ark_ff::PrimeField;
use ark_relations::r1cs::ConstraintSystem;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::rand::Rng;

use super::Arith;
use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, SparseMatrix};
use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, vec_sub, SparseMatrix};
use crate::Error;

#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
Expand Down Expand Up @@ -92,6 +97,84 @@ impl<F: PrimeField> RelaxedR1CS<F> {

Ok(())
}

// Computes the E term, given A, B, C, z, u
fn compute_E(
A: &SparseMatrix<F>,
B: &SparseMatrix<F>,
C: &SparseMatrix<F>,
z: &[F],
u: &F,
) -> Result<Vec<F>, Error> {
let Az = mat_vec_mul(A, z)?;
let Bz = mat_vec_mul(B, z)?;
let AzBz = hadamard(&Az, &Bz)?;

let Cz = mat_vec_mul(C, z)?;
let uCz = vec_scalar_mul(&Cz, u);
vec_sub(&AzBz, &uCz)
}

pub fn check_sampled_relaxed_r1cs(&self, u: F, E: &[F], z: &[F]) -> bool {
let sampled = RelaxedR1CS {
l: self.l,
A: self.A.clone(),
B: self.B.clone(),
C: self.C.clone(),
u,
E: E.to_vec(),
};
sampled.check_relation(z).is_ok()
}

// Implements sampling a (committed) RelaxedR1CS
// See construction 5 in https://eprint.iacr.org/2023/573.pdf
pub fn sample<C, CS>(
&self,
params: &CS::ProverParams,
mut rng: impl RngCore,
) -> Result<(CommittedInstance<C>, Witness<C>), Error>
where
C: CurveGroup,
C: CurveGroup<ScalarField = F>,
<C as Group>::ScalarField: Absorb,
CS: CommitmentScheme<C, true>,
{
let u = C::ScalarField::rand(&mut rng);
let rE = C::ScalarField::rand(&mut rng);
let rW = C::ScalarField::rand(&mut rng);

let W = (0..self.A.n_cols - self.l - 1)
.map(|_| F::rand(&mut rng))
.collect();
let x = (0..self.l).map(|_| F::rand(&mut rng)).collect::<Vec<F>>();
let mut z = vec![u];
z.extend(&x);
z.extend(&W);

let E = RelaxedR1CS::compute_E(&self.A, &self.B, &self.C, &z, &u)?;

debug_assert!(
z.len() == self.A.n_cols,
"Length of z is {}, while A has {} columns.",
z.len(),
self.A.n_cols
);

debug_assert!(
self.check_sampled_relaxed_r1cs(u, &E, &z),
"Sampled a non satisfiable relaxed R1CS, sampled u: {}, computed E: {:?}",
u,
E
);

let witness = Witness { E, rE, W, rW };
let mut cm_witness = witness.commit::<CS, true>(params, x)?;

// witness.commit() sets u to 1, we set it to the sampled u value
cm_witness.u = u;
Ok((cm_witness, witness))
}
}

/// extracts arkworks ConstraintSystem matrices into crate::utils::vec::SparseMatrix format as R1CS
Expand Down Expand Up @@ -138,9 +221,24 @@ pub fn extract_w_x<F: PrimeField>(cs: &ConstraintSystem<F>) -> (Vec<F>, Vec<F>)
#[cfg(test)]
pub mod tests {
use super::*;
use crate::utils::vec::tests::{to_F_matrix, to_F_vec};
use crate::{
commitment::pedersen::Pedersen,
utils::vec::tests::{to_F_matrix, to_F_vec},
};

use ark_pallas::Fr;
use ark_pallas::{Fr, Projective};

#[test]
pub fn sample_relaxed_r1cs() {
let rng = rand::rngs::OsRng;
let r1cs = get_test_r1cs::<Fr>();
let (prover_params, _) = Pedersen::<Projective>::setup(rng, r1cs.A.n_rows).unwrap();

let relaxed_r1cs = r1cs.relax();
let sampled =
relaxed_r1cs.sample::<Projective, Pedersen<Projective, true>>(&prover_params, rng);
assert!(sampled.is_ok());
}

pub fn get_test_r1cs<F: PrimeField>() -> R1CS<F> {
// R1CS for: x^3 + x + 5 = y (example from article
Expand Down
21 changes: 15 additions & 6 deletions folding-schemes/src/folding/nova/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub mod decider_eth_circuit;
pub mod nifs;
pub mod serialize;
pub mod traits;

pub mod zk;
use circuits::{AugmentedFCircuit, ChallengeGadget};
use nifs::NIFS;
use traits::NovaR1CS;
Expand Down Expand Up @@ -957,25 +957,33 @@ pub mod tests {
test_ivc_opt::<Pedersen<Projective>, Pedersen<Projective2>, false>(
poseidon_config.clone(),
F_circuit,
3,
);

test_ivc_opt::<Pedersen<Projective, true>, Pedersen<Projective2, true>, true>(
poseidon_config.clone(),
F_circuit,
3,
);

// run the test using KZG for the commitments on the main curve, and Pedersen for the
// commitments on the secondary curve
test_ivc_opt::<KZG<Bn254>, Pedersen<Projective2>, false>(poseidon_config, F_circuit);
test_ivc_opt::<KZG<Bn254>, Pedersen<Projective2>, false>(poseidon_config, F_circuit, 3);
}

// test_ivc allowing to choose the CommitmentSchemes
fn test_ivc_opt<
#[allow(clippy::type_complexity)]
pub(crate) fn test_ivc_opt<
CS1: CommitmentScheme<Projective, H>,
CS2: CommitmentScheme<Projective2, H>,
const H: bool,
>(
poseidon_config: PoseidonConfig<Fr>,
F_circuit: CubicFCircuit<Fr>,
num_steps: usize,
) -> (
Vec<Fr>,
Nova<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2, H>,
) {
let mut rng = ark_std::test_rng();

Expand Down Expand Up @@ -1009,7 +1017,6 @@ pub mod tests {
)
.unwrap();

let num_steps: usize = 3;
for _ in 0..num_steps {
nova.prove_step(&mut rng, vec![], None).unwrap();
}
Expand All @@ -1018,13 +1025,15 @@ pub mod tests {
let (running_instance, incoming_instance, cyclefold_instance) = nova.instances();
Nova::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2, H>::verify(
nova_params.1, // Nova's verifier params
z_0,
nova.z_i,
z_0.clone(),
nova.z_i.clone(),
nova.i,
running_instance,
incoming_instance,
cyclefold_instance,
)
.unwrap();

(z_0, nova)
}
}
Loading

0 comments on commit c09c52f

Please sign in to comment.