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

Improve the design of circuit, config, and instances for CycleFold #158

Draft
wants to merge 29 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
aa89847
Parallelize vector and matrix operations
winderica Jul 23, 2024
82d472b
Implement convenient methods for `NonNativeAffineVar`
winderica Jul 23, 2024
73cd5d7
Return `L_X_evals` and intermediate `phi_star`s from ProtoGalaxy prover.
winderica Jul 23, 2024
b5c2c47
Correctly use number of variables, number of constraints, and `t`
winderica Jul 23, 2024
f46430f
Fix the size of `F_coeffs` and `K_coeffs` for in-circuit consistency
winderica Jul 23, 2024
b2b7ae8
Improve prover's performance
winderica Jul 23, 2024
972a035
Make `prepare_inputs` generic
winderica Jul 23, 2024
4977885
Remove redundant parameters in verifier
winderica Jul 23, 2024
b6593e7
Move `eval_f` to arith
winderica Jul 23, 2024
0cdcfa3
`u` is unnecessary in ProtoGalaxy
winderica Jul 23, 2024
ea47553
Convert `RelaxedR1CS` to a trait that can be used in both Nova and Pr…
winderica Jul 23, 2024
0a83094
Implement several traits for ProtoGalaxy
winderica Jul 23, 2024
df191a9
Move `FCircuit` impls to `utils.rs` and add `DummyCircuit`
winderica Jul 23, 2024
cc7b5bc
`AugmentedFCircuit` and ProtoGalaxy-based IVC
winderica Aug 5, 2024
03ae498
Add explanations about IVC prover and in-circuit operations
winderica Aug 5, 2024
243907c
Avoid using unstable features
winderica Aug 5, 2024
3be7683
Rename `PROTOGALAXY` to `PG` to make clippy happy
winderica Aug 5, 2024
0980f41
Fix merge conflicts in `RelaxedR1CS::sample`
winderica Sep 7, 2024
c230f0a
Fix merge conflicts in `CycleFoldCircuit`
winderica Sep 7, 2024
1c9257b
Swap `m` and `n` for protogalaxy
winderica Sep 7, 2024
f83054e
Add `#[cfg(test)]` to test-only util circuits
winderica Sep 7, 2024
5ee5490
Prefer unit struct over empty struct
winderica Sep 7, 2024
7f543ab
Add documents to `AugmentedFCircuit` for ProtoGalaxy
winderica Sep 7, 2024
3191a92
Fix the names for CycleFold cricuits in ProtoGalaxy
winderica Sep 7, 2024
eca4849
Fix usize conversion when targeting wasm
winderica Sep 7, 2024
5483ad0
Restrict the visibility of fields in `AugmentedFCircuit` to `pub(super)`
winderica Sep 8, 2024
82f473a
Make CycleFold circuits and configs public
winderica Sep 8, 2024
d779889
Add docs for `ProverParams` and `VerifierParams`
winderica Sep 8, 2024
bb0dd4f
Add `CycleFoldCommittedInstanceVar::new_incoming_from_components` to …
winderica Sep 11, 2024
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
32 changes: 20 additions & 12 deletions folding-schemes/src/arith/ccs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ pub struct CCS<F: PrimeField> {
}

impl<F: PrimeField> Arith<F> for CCS<F> {
/// check that a CCS structure is satisfied by a z vector. Only for testing.
fn check_relation(&self, z: &[F]) -> Result<(), Error> {
fn eval_relation(&self, z: &[F]) -> Result<Vec<F>, Error> {
let mut result = vec![F::zero(); self.m];

for i in 0..self.q {
Expand All @@ -57,14 +56,7 @@ impl<F: PrimeField> Arith<F> for CCS<F> {
result = vec_add(&result, &c_M_j_z)?;
}

// make sure the final vector is all zeroes
for e in result {
if !e.is_zero() {
return Err(Error::NotSatisfied);
}
}

Ok(())
Ok(result)
}

fn params_to_le_bytes(&self) -> Vec<u8> {
Expand Down Expand Up @@ -113,7 +105,10 @@ impl<F: PrimeField> CCS<F> {
#[cfg(test)]
pub mod tests {
use super::*;
use crate::arith::r1cs::tests::{get_test_r1cs, get_test_z as r1cs_get_test_z};
use crate::{
arith::r1cs::tests::{get_test_r1cs, get_test_z as r1cs_get_test_z},
utils::vec::is_zero_vec,
};
use ark_pallas::Fr;

pub fn get_test_ccs<F: PrimeField>() -> CCS<F> {
Expand All @@ -124,9 +119,22 @@ pub mod tests {
r1cs_get_test_z(input)
}

#[test]
fn test_eval_ccs_relation() {
let ccs = get_test_ccs::<Fr>();
let mut z = get_test_z(3);

let f_w = ccs.eval_relation(&z).unwrap();
assert!(is_zero_vec(&f_w));

z[1] = Fr::from(111);
let f_w = ccs.eval_relation(&z).unwrap();
assert!(!is_zero_vec(&f_w));
}

/// Test that a basic CCS relation can be satisfied
#[test]
fn test_ccs_relation() {
fn test_check_ccs_relation() {
let ccs = get_test_ccs::<Fr>();
let z = get_test_z(3);

Expand Down
17 changes: 15 additions & 2 deletions folding-schemes/src/arith/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,21 @@ pub mod ccs;
pub mod r1cs;

pub trait Arith<F: PrimeField> {
/// Checks that the given Arith structure is satisfied by a z vector. Used only for testing.
fn check_relation(&self, z: &[F]) -> Result<(), Error>;
/// Evaluate the given Arith structure at `z`, a vector of assignments, and
/// return the evaluation.
fn eval_relation(&self, z: &[F]) -> Result<Vec<F>, Error>;

/// Checks that the given Arith structure is satisfied by a z vector, i.e.,
/// if the evaluation is a zero vector
///
/// Used only for testing.
fn check_relation(&self, z: &[F]) -> Result<(), Error> {
if self.eval_relation(z)?.iter().all(|f| f.is_zero()) {
Ok(())
} else {
Err(Error::NotSatisfied)
}
}

/// Returns the bytes that represent the parameters, that is, the matrices sizes, the amount of
/// public inputs, etc, without the matrices/polynomials values.
Expand Down
190 changes: 75 additions & 115 deletions folding-schemes/src/arith/r1cs.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
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_ec::CurveGroup;
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, vec_sub, SparseMatrix};
use crate::utils::vec::{hadamard, mat_vec_mul, vec_scalar_mul, vec_sub, SparseMatrix};
use crate::Error;

#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
Expand All @@ -21,16 +19,24 @@ pub struct R1CS<F: PrimeField> {
}

impl<F: PrimeField> Arith<F> for R1CS<F> {
/// check that a R1CS structure is satisfied by a z vector. Only for testing.
fn check_relation(&self, z: &[F]) -> Result<(), Error> {
fn eval_relation(&self, z: &[F]) -> Result<Vec<F>, Error> {
if z.len() != self.A.n_cols {
return Err(Error::NotSameLength(
"z.len()".to_string(),
z.len(),
"number of variables in R1CS".to_string(),
self.A.n_cols,
));
}

let Az = mat_vec_mul(&self.A, z)?;
let Bz = mat_vec_mul(&self.B, z)?;
let Cz = mat_vec_mul(&self.C, z)?;
// Multiply Cz by z[0] (u) here, allowing this method to be reused for
// both relaxed and unrelaxed R1CS.
let uCz = vec_scalar_mul(&Cz, &z[0]);
let AzBz = hadamard(&Az, &Bz)?;
if AzBz != Cz {
return Err(Error::NotSatisfied);
}
Ok(())
vec_sub(&AzBz, &uCz)
}

fn params_to_le_bytes(&self) -> Vec<u8> {
Expand All @@ -57,55 +63,50 @@ impl<F: PrimeField> R1CS<F> {
pub fn split_z(&self, z: &[F]) -> (Vec<F>, Vec<F>) {
(z[self.l + 1..].to_vec(), z[1..self.l + 1].to_vec())
}

/// converts the R1CS instance into a RelaxedR1CS as described in
/// [Nova](https://eprint.iacr.org/2021/370.pdf) section 4.1.
pub fn relax(self) -> RelaxedR1CS<F> {
RelaxedR1CS::<F> {
l: self.l,
E: vec![F::zero(); self.A.n_rows],
A: self.A,
B: self.B,
C: self.C,
u: F::one(),
}
}
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct RelaxedR1CS<F: PrimeField> {
pub l: usize, // io len
pub A: SparseMatrix<F>,
pub B: SparseMatrix<F>,
pub C: SparseMatrix<F>,
pub u: F,
pub E: Vec<F>,
}
pub trait RelaxedR1CS<C: CurveGroup, W, U>: Arith<C::ScalarField> {
/// returns a dummy running instance (Witness and CommittedInstance) for the current R1CS structure
fn dummy_running_instance(&self) -> (W, U);

impl<F: PrimeField> RelaxedR1CS<F> {
/// check that a RelaxedR1CS structure is satisfied by a z vector. Only for testing.
pub fn check_relation(&self, z: &[F]) -> Result<(), Error> {
let Az = mat_vec_mul(&self.A, z)?;
let Bz = mat_vec_mul(&self.B, z)?;
let Cz = mat_vec_mul(&self.C, z)?;
let uCz = vec_scalar_mul(&Cz, &self.u);
let uCzE = vec_add(&uCz, &self.E)?;
let AzBz = hadamard(&Az, &Bz)?;
if AzBz != uCzE {
return Err(Error::NotSatisfied);
/// returns a dummy incoming instance (Witness and CommittedInstance) for the current R1CS structure
fn dummy_incoming_instance(&self) -> (W, U);

/// checks if the given instance is relaxed
fn is_relaxed(w: &W, u: &U) -> bool;

/// extracts `z`, the vector of variables, from the given Witness and CommittedInstance
fn extract_z(w: &W, u: &U) -> Vec<C::ScalarField>;

/// checks if the computed error terms correspond to the actual one in `w`
/// or `u`
fn check_error_terms(w: &W, u: &U, e: Vec<C::ScalarField>) -> Result<(), Error>;

/// checks the tight (unrelaxed) R1CS relation
fn check_tight_relation(&self, w: &W, u: &U) -> Result<(), Error> {
if Self::is_relaxed(w, u) {
return Err(Error::R1CSUnrelaxedFail);
}

Ok(())
let z = Self::extract_z(w, u);
self.check_relation(&z)
}

/// checks the relaxed R1CS relation
fn check_relaxed_relation(&self, w: &W, u: &U) -> Result<(), Error> {
let z = Self::extract_z(w, u);
let e = self.eval_relation(&z)?;
Self::check_error_terms(w, u, e)
}

// 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> {
A: &SparseMatrix<C::ScalarField>,
B: &SparseMatrix<C::ScalarField>,
C: &SparseMatrix<C::ScalarField>,
z: &[C::ScalarField],
u: &C::ScalarField,
) -> Result<Vec<C::ScalarField>, Error> {
let Az = mat_vec_mul(A, z)?;
let Bz = mat_vec_mul(B, z)?;
let AzBz = hadamard(&Az, &Bz)?;
Expand All @@ -115,66 +116,9 @@ impl<F: PrimeField> RelaxedR1CS<F> {
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>
fn sample<CS>(&self, params: &CS::ProverParams, rng: impl RngCore) -> Result<(W, U), 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))
}
CS: CommitmentScheme<C, true>;
}

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

use ark_pallas::{Fr, Projective};
Expand All @@ -234,9 +182,8 @@ pub mod tests {
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);
let sampled: Result<(Witness<Projective>, CommittedInstance<Projective>), _> =
r1cs.sample::<Pedersen<Projective, true>>(&prover_params, rng);
assert!(sampled.is_ok());
}

Expand Down Expand Up @@ -294,10 +241,23 @@ pub mod tests {
}

#[test]
fn test_check_relation() {
fn test_eval_r1cs_relation() {
let mut rng = ark_std::test_rng();
let r1cs = get_test_r1cs::<Fr>();
let mut z = get_test_z::<Fr>(rng.gen::<u16>() as usize);

let f_w = r1cs.eval_relation(&z).unwrap();
assert!(is_zero_vec(&f_w));

z[1] = Fr::from(111);
let f_w = r1cs.eval_relation(&z).unwrap();
assert!(!is_zero_vec(&f_w));
}

#[test]
fn test_check_r1cs_relation() {
let r1cs = get_test_r1cs::<Fr>();
let z = get_test_z(5);
r1cs.check_relation(&z).unwrap();
r1cs.relax().check_relation(&z).unwrap();
}
}
Loading