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

Protogalaxy based IVC #123

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8762888
Parallelize vector and matrix operations
winderica Jul 23, 2024
9f9d5f8
Implement convenient methods for `NonNativeAffineVar`
winderica Jul 23, 2024
9b8edc7
Return `L_X_evals` and intermediate `phi_star`s from ProtoGalaxy prover.
winderica Jul 23, 2024
e7858d9
Correctly use number of variables, number of constraints, and `t`
winderica Jul 23, 2024
fcba338
Fix the size of `F_coeffs` and `K_coeffs` for in-circuit consistency
winderica Jul 23, 2024
3d3eb66
Improve prover's performance
winderica Jul 23, 2024
a4f9625
Make `prepare_inputs` generic
winderica Jul 23, 2024
482e810
Remove redundant parameters in verifier
winderica Jul 23, 2024
2b91558
Move `eval_f` to arith
winderica Jul 23, 2024
c936b2f
`u` is unnecessary in ProtoGalaxy
winderica Jul 23, 2024
92ed264
Convert `RelaxedR1CS` to a trait that can be used in both Nova and Pr…
winderica Jul 23, 2024
29e9641
Implement several traits for ProtoGalaxy
winderica Jul 23, 2024
5bb3231
Move `FCircuit` impls to `utils.rs` and add `DummyCircuit`
winderica Jul 23, 2024
6e5d5e6
`AugmentedFCircuit` and ProtoGalaxy-based IVC
winderica Aug 5, 2024
0945a58
Add explanations about IVC prover and in-circuit operations
winderica Aug 5, 2024
05712a4
Avoid using unstable features
winderica Aug 5, 2024
963234c
Rename `PROTOGALAXY` to `PG` to make clippy happy
winderica Aug 5, 2024
98ecd91
Fix merge conflicts in `RelaxedR1CS::sample`
winderica Sep 7, 2024
d52d719
Fix merge conflicts in `CycleFoldCircuit`
winderica Sep 7, 2024
b01a789
Swap `m` and `n` for protogalaxy
winderica Sep 7, 2024
6c4b1eb
Add `#[cfg(test)]` to test-only util circuits
winderica Sep 7, 2024
39cb647
Prefer unit struct over empty struct
winderica Sep 7, 2024
f79fcba
Add documents to `AugmentedFCircuit` for ProtoGalaxy
winderica Sep 7, 2024
0ab0332
Fix the names for CycleFold cricuits in ProtoGalaxy
winderica Sep 7, 2024
8d4b41f
Fix usize conversion when targeting wasm
winderica Sep 7, 2024
7d44492
Restrict the visibility of fields in `AugmentedFCircuit` to `pub(super)`
winderica Sep 8, 2024
c15e15c
Make CycleFold circuits and configs public
winderica Sep 8, 2024
b9696bf
Add docs for `ProverParams` and `VerifierParams`
winderica Sep 8, 2024
2269ac9
Refactor `pow_i`
winderica Sep 11, 2024
9c7e258
Fix imports
winderica Sep 12, 2024
012c6ca
Remove lint reasons
winderica Sep 12, 2024
d24455b
Fix type inference
winderica Sep 12, 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 Down Expand Up @@ -65,55 +71,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 @@ -123,66 +124,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 @@ -229,9 +173,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 @@ -242,9 +190,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 @@ -302,10 +249,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();
}
}
9 changes: 7 additions & 2 deletions folding-schemes/src/folding/circuits/cyclefold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,8 @@ where
}
}

/// `CycleFoldConfig` allows us to customize the behavior of CycleFold circuit
/// according to the folding scheme we are working with.
pub trait CycleFoldConfig {
/// `N_INPUT_POINTS` specifies the number of input points that are folded in
/// [`CycleFoldCircuit`] via random linear combinations.
Expand Down Expand Up @@ -465,7 +467,10 @@ where
// In multifolding schemes such as HyperNova, this is:
// computed_x = [r, p_0, p_1, p_2, ..., p_n, p_folded],
// where each p_i is in fact p_i.to_constraint_field()
let r_fp = Boolean::le_bits_to_fp_var(&r_bits)?;
let r_fp = r_bits
.chunks(CFG::F::MODULUS_BIT_SIZE as usize - 1)
.map(Boolean::le_bits_to_fp_var)
.collect::<Result<Vec<_>, _>>()?;
let points_aux: Vec<FpVar<CFG::F>> = points
.iter()
.map(|p_i| Ok(p_i.to_constraint_field()?[..2].to_vec()))
Expand All @@ -475,7 +480,7 @@ where
.collect();

let computed_x: Vec<FpVar<CFG::F>> = [
vec![r_fp],
r_fp,
points_aux,
p_folded.to_constraint_field()?[..2].to_vec(),
]
Expand Down
Loading
Loading