From f1d82418ba047cf90805f2d0505370246df24d68 Mon Sep 17 00:00:00 2001 From: winderica Date: Wed, 25 Sep 2024 18:21:30 +0800 Subject: [PATCH] Refactor `Arith` trait (#162) * `Dummy` trait * More generic design for `Arith` * Distinguish between incoming and running instances in ProtoGalaxy * Format * Fix unit test * Fix incorrect arguments supplied to `CycleFoldWitness::dummy` * `RUNNING` and `INCOMING` constants * Better name and docs for `eval_core` * More docs for `Arith` methods and implementations * Fix missing imports --- folding-schemes/src/arith/ccs.rs | 65 +++++--- folding-schemes/src/arith/mod.rs | 156 ++++++++++++++++-- folding-schemes/src/arith/r1cs.rs | 143 +++++++--------- folding-schemes/src/folding/hypernova/cccs.rs | 53 +++--- .../src/folding/hypernova/circuits.rs | 41 ++--- .../folding/hypernova/decider_eth_circuit.rs | 19 +-- .../src/folding/hypernova/lcccs.rs | 63 +++---- folding-schemes/src/folding/hypernova/mod.rs | 58 ++++--- .../src/folding/hypernova/nimfs.rs | 26 +-- .../src/folding/hypernova/utils.rs | 12 +- folding-schemes/src/folding/nova/circuits.rs | 5 +- .../src/folding/nova/decider_circuits.rs | 14 +- .../src/folding/nova/decider_eth_circuit.rs | 48 +++--- folding-schemes/src/folding/nova/mod.rs | 74 +++++---- folding-schemes/src/folding/nova/nifs.rs | 33 ++-- folding-schemes/src/folding/nova/traits.rs | 91 +++++----- folding-schemes/src/folding/nova/zk.rs | 14 +- .../src/folding/protogalaxy/circuits.rs | 20 +-- .../src/folding/protogalaxy/constants.rs | 4 + .../src/folding/protogalaxy/folding.rs | 56 ++++--- .../src/folding/protogalaxy/mod.rs | 134 ++++++++++----- .../src/folding/protogalaxy/traits.rs | 109 ++++++------ folding-schemes/src/folding/traits.rs | 10 ++ folding-schemes/src/utils/mod.rs | 6 +- 24 files changed, 723 insertions(+), 531 deletions(-) create mode 100644 folding-schemes/src/folding/protogalaxy/constants.rs diff --git a/folding-schemes/src/arith/ccs.rs b/folding-schemes/src/arith/ccs.rs index 10425f8..e1c9575 100644 --- a/folding-schemes/src/arith/ccs.rs +++ b/folding-schemes/src/arith/ccs.rs @@ -1,9 +1,12 @@ use ark_ff::PrimeField; use ark_std::log2; -use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, SparseMatrix}; +use crate::utils::vec::{ + hadamard, is_zero_vec, mat_vec_mul, vec_add, vec_scalar_mul, SparseMatrix, +}; use crate::Error; +use super::ArithSerializer; use super::{r1cs::R1CS, Arith}; /// CCS represents the Customizable Constraint Systems structure defined in @@ -35,8 +38,9 @@ pub struct CCS { pub c: Vec, } -impl Arith for CCS { - fn eval_relation(&self, z: &[F]) -> Result, Error> { +impl CCS { + /// Evaluates the CCS relation at a given vector of assignments `z` + pub fn eval_at_z(&self, z: &[F]) -> Result, Error> { let mut result = vec![F::zero(); self.m]; for i in 0..self.q { @@ -59,6 +63,25 @@ impl Arith for CCS { Ok(result) } + /// returns a tuple containing (w, x) (witness and public inputs respectively) + pub fn split_z(&self, z: &[F]) -> (Vec, Vec) { + (z[self.l + 1..].to_vec(), z[1..self.l + 1].to_vec()) + } +} + +impl, U: AsRef<[F]>> Arith for CCS { + type Evaluation = Vec; + + fn eval_relation(&self, w: &W, u: &U) -> Result { + self.eval_at_z(&[&[F::one()], u.as_ref(), w.as_ref()].concat()) + } + + fn check_evaluation(_w: &W, _u: &U, e: Self::Evaluation) -> Result<(), Error> { + is_zero_vec(&e).then_some(()).ok_or(Error::NotSatisfied) + } +} + +impl ArithSerializer for CCS { fn params_to_le_bytes(&self) -> Vec { [ self.l.to_le_bytes(), @@ -72,14 +95,14 @@ impl Arith for CCS { } } -impl CCS { - pub fn from_r1cs(r1cs: R1CS) -> Self { - let m = r1cs.A.n_rows; - let n = r1cs.A.n_cols; +impl From> for CCS { + fn from(r1cs: R1CS) -> Self { + let m = r1cs.num_constraints(); + let n = r1cs.num_variables(); CCS { m, n, - l: r1cs.l, + l: r1cs.num_public_inputs(), s: log2(m) as usize, s_prime: log2(n) as usize, t: 3, @@ -91,29 +114,19 @@ impl CCS { M: vec![r1cs.A, r1cs.B, r1cs.C], } } - - pub fn to_r1cs(self) -> R1CS { - R1CS:: { - l: self.l, - A: self.M[0].clone(), - B: self.M[1].clone(), - C: self.M[2].clone(), - } - } } #[cfg(test)] pub mod tests { use super::*; use crate::{ - arith::r1cs::tests::{get_test_r1cs, get_test_z as r1cs_get_test_z}, + arith::r1cs::tests::{get_test_r1cs, get_test_z as r1cs_get_test_z, get_test_z_split}, utils::vec::is_zero_vec, }; use ark_pallas::Fr; pub fn get_test_ccs() -> CCS { - let r1cs = get_test_r1cs::(); - CCS::::from_r1cs(r1cs) + get_test_r1cs::().into() } pub fn get_test_z(input: usize) -> Vec { r1cs_get_test_z(input) @@ -122,13 +135,13 @@ pub mod tests { #[test] fn test_eval_ccs_relation() { let ccs = get_test_ccs::(); - let mut z = get_test_z(3); + let (_, x, mut w) = get_test_z_split(3); - let f_w = ccs.eval_relation(&z).unwrap(); + let f_w = ccs.eval_relation(&w, &x).unwrap(); assert!(is_zero_vec(&f_w)); - z[1] = Fr::from(111); - let f_w = ccs.eval_relation(&z).unwrap(); + w[1] = Fr::from(111); + let f_w = ccs.eval_relation(&w, &x).unwrap(); assert!(!is_zero_vec(&f_w)); } @@ -136,8 +149,8 @@ pub mod tests { #[test] fn test_check_ccs_relation() { let ccs = get_test_ccs::(); - let z = get_test_z(3); + let (_, x, w) = get_test_z_split(3); - ccs.check_relation(&z).unwrap(); + ccs.check_relation(&w, &x).unwrap(); } } diff --git a/folding-schemes/src/arith/mod.rs b/folding-schemes/src/arith/mod.rs index d1fa8a3..b5ebd7b 100644 --- a/folding-schemes/src/arith/mod.rs +++ b/folding-schemes/src/arith/mod.rs @@ -1,28 +1,156 @@ -use ark_ff::PrimeField; +use ark_ec::CurveGroup; +use ark_relations::r1cs::SynthesisError; +use ark_std::rand::RngCore; -use crate::Error; +use crate::{commitment::CommitmentScheme, folding::traits::Dummy, Error}; pub mod ccs; pub mod r1cs; -pub trait Arith { - /// Evaluate the given Arith structure at `z`, a vector of assignments, and - /// return the evaluation. - fn eval_relation(&self, z: &[F]) -> Result, Error>; +/// `Arith` defines the operations that a constraint system (e.g., R1CS, CCS, +/// etc.) should support. +/// +/// Here, `W` is the type of witness, and `U` is the type of statement / public +/// input / public IO / instance. +/// Note that the same constraint system may support different types of `W` and +/// `U`, and the satisfiability check may vary. +/// +/// For example, both plain R1CS and relaxed R1CS are represented by 3 matrices, +/// but the types of `W` and `U` are different: +/// - The plain R1CS has `W` and `U` as vectors of field elements. +/// +/// `W = w` and `U = x` satisfy R1CS if `Az ∘ Bz = Cz`, where `z = [1, x, w]`. +/// +/// - In Nova, Relaxed R1CS has `W` as [`crate::folding::nova::Witness`], +/// and `U` as [`crate::folding::nova::CommittedInstance`]. +/// +/// `W = (w, e, ...)` and `U = (u, x, ...)` satisfy Relaxed R1CS if +/// `Az ∘ Bz = uCz + e`, where `z = [u, x, w]`. +/// (commitments in `U` are not checked here) +/// +/// Also, `W` and `U` have non-native field elements as their components when +/// used as CycleFold witness and instance. +/// +/// - In ProtoGalaxy, Relaxed R1CS has `W` as [`crate::folding::protogalaxy::Witness`], +/// and `U` as [`crate::folding::protogalaxy::CommittedInstance`]. +/// +/// `W = (w, ...)` and `U = (x, e, β, ...)` satisfy Relaxed R1CS if +/// `e = Σ pow_i(β) v_i`, where `v = Az ∘ Bz - Cz`, `z = [1, x, w]`. +/// (commitments in `U` are not checked here) +/// +/// This is also the case of CCS, where `W` and `U` may be vectors of field +/// elements, [`crate::folding::hypernova::Witness`] and [`crate::folding::hypernova::lcccs::LCCCS`], +/// or [`crate::folding::hypernova::Witness`] and [`crate::folding::hypernova::cccs::CCCS`]. +pub trait Arith: Clone { + type Evaluation; - /// Checks that the given Arith structure is satisfied by a z vector, i.e., - /// if the evaluation is a zero vector + /// Returns a dummy witness and instance + fn dummy_witness_instance<'a>(&'a self) -> (W, U) + where + W: Dummy<&'a Self>, + U: Dummy<&'a Self>, + { + (W::dummy(self), U::dummy(self)) + } + + /// Evaluates the constraint system `self` at witness `w` and instance `u`. + /// Returns the evaluation result. + /// + /// The evaluation result is usually a vector of field elements. + /// For instance: + /// - Evaluating the plain R1CS at `W = w` and `U = x` returns + /// `Az ∘ Bz - Cz`, where `z = [1, x, w]`. + /// + /// - Evaluating the relaxed R1CS in Nova at `W = (w, e, ...)` and + /// `U = (u, x, ...)` returns `Az ∘ Bz - uCz`, where `z = [u, x, w]`. + /// + /// - Evaluating the relaxed R1CS in ProtoGalaxy at `W = (w, ...)` and + /// `U = (x, e, β, ...)` returns `Az ∘ Bz - Cz`, where `z = [1, x, w]`. + /// + /// However, we use `Self::Evaluation` to represent the evaluation result + /// for future extensibility. + fn eval_relation(&self, w: &W, u: &U) -> Result; + + /// Checks if the evaluation result is valid. The witness `w` and instance + /// `u` are also parameters, because the validity check may need information + /// contained in `w` and/or `u`. + /// + /// For instance: + /// - The evaluation `v` of plain R1CS at satisfying `W` and `U` should be + /// an all-zero vector. + /// + /// - The evaluation `v` of relaxed R1CS in Nova at satisfying `W` and `U` + /// should be equal to the error term `e` in the witness. + /// + /// - The evaluation `v` of relaxed R1CS in ProtoGalaxy at satisfying `W` + /// and `U` should satisfy `e = Σ pow_i(β) v_i`, where `e` is the error + /// term in the committed instance. + fn check_evaluation(w: &W, u: &U, v: Self::Evaluation) -> Result<(), Error>; + + /// Checks if witness `w` and instance `u` satisfy the constraint system + /// `self` by first computing the evaluation result and then checking the + /// validity of the evaluation result. /// /// 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) - } + fn check_relation(&self, w: &W, u: &U) -> Result<(), Error> { + let e = self.eval_relation(w, u)?; + Self::check_evaluation(w, u, e) } +} +/// `ArithSerializer` is for serializing constraint systems. +/// +/// Currently we only support converting parameters to bytes, but in the future +/// we may consider implementing methods for serializing the actual data (e.g., +/// R1CS matrices). +pub trait ArithSerializer { /// Returns the bytes that represent the parameters, that is, the matrices sizes, the amount of /// public inputs, etc, without the matrices/polynomials values. fn params_to_le_bytes(&self) -> Vec; } + +/// `ArithSampler` allows sampling random pairs of witness and instance that +/// satisfy the constraint system `self`. +/// +/// This is useful for constructing a zero-knowledge layer for a folding-based +/// IVC. +/// An example of such a layer can be found in Appendix D of the [HyperNova] +/// paper. +/// +/// Note that we use a separate trait for sampling, because this operation may +/// not be supported by all witness-instance pairs. +/// For instance, it is difficult (if not impossible) to do this for `w` and `x` +/// in a plain R1CS. +/// +/// [HyperNova]: https://eprint.iacr.org/2023/573.pdf +pub trait ArithSampler: Arith { + /// Samples a random witness and instance that satisfy the constraint system. + fn sample_witness_instance>( + &self, + params: &CS::ProverParams, + rng: impl RngCore, + ) -> Result<(W, U), Error>; +} + +/// `ArithGadget` defines the in-circuit counterparts of operations specified in +/// `Arith` on constraint systems. +pub trait ArithGadget { + type Evaluation; + + /// Evaluates the constraint system `self` at witness `w` and instance `u`. + /// Returns the evaluation result. + fn eval_relation(&self, w: &WVar, u: &UVar) -> Result; + + /// Generates constraints for enforcing that witness `w` and instance `u` + /// satisfy the constraint system `self` by first computing the evaluation + /// result and then checking the validity of the evaluation result. + fn enforce_relation(&self, w: &WVar, u: &UVar) -> Result<(), SynthesisError> { + let e = self.eval_relation(w, u)?; + Self::enforce_evaluation(w, u, e) + } + + /// Generates constraints for enforcing that the evaluation result is valid. + /// The witness `w` and instance `u` are also parameters, because the + /// validity check may need information contained in `w` and/or `u`. + fn enforce_evaluation(w: &WVar, u: &UVar, e: Self::Evaluation) -> Result<(), SynthesisError>; +} diff --git a/folding-schemes/src/arith/r1cs.rs b/folding-schemes/src/arith/r1cs.rs index 4643cd9..164528c 100644 --- a/folding-schemes/src/arith/r1cs.rs +++ b/folding-schemes/src/arith/r1cs.rs @@ -1,13 +1,13 @@ -use crate::commitment::CommitmentScheme; -use crate::RngCore; -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_scalar_mul, vec_sub, SparseMatrix}; +use super::ccs::CCS; +use super::{Arith, ArithSerializer}; +use crate::utils::vec::{ + hadamard, is_zero_vec, mat_vec_mul, vec_scalar_mul, vec_sub, SparseMatrix, +}; use crate::Error; #[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)] @@ -18,8 +18,9 @@ pub struct R1CS { pub C: SparseMatrix, } -impl Arith for R1CS { - fn eval_relation(&self, z: &[F]) -> Result, Error> { +impl R1CS { + /// Evaluates the CCS relation at a given vector of variables `z` + pub fn eval_at_z(&self, z: &[F]) -> Result, Error> { if z.len() != self.A.n_cols { return Err(Error::NotSameLength( "z.len()".to_string(), @@ -33,12 +34,26 @@ impl Arith for R1CS { 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. + // both relaxed and plain R1CS. let uCz = vec_scalar_mul(&Cz, &z[0]); let AzBz = hadamard(&Az, &Bz)?; vec_sub(&AzBz, &uCz) } +} + +impl, U: AsRef<[F]>> Arith for R1CS { + type Evaluation = Vec; + + fn eval_relation(&self, w: &W, u: &U) -> Result { + self.eval_at_z(&[&[F::one()], u.as_ref(), w.as_ref()].concat()) + } + fn check_evaluation(_w: &W, _u: &U, e: Self::Evaluation) -> Result<(), Error> { + is_zero_vec(&e).then_some(()).ok_or(Error::NotSatisfied) + } +} + +impl ArithSerializer for R1CS { fn params_to_le_bytes(&self) -> Vec { [ self.l.to_le_bytes(), @@ -67,66 +82,41 @@ impl R1CS { } } - /// returns a tuple containing (w, x) (witness and public inputs respectively) - pub fn split_z(&self, z: &[F]) -> (Vec, Vec) { - (z[self.l + 1..].to_vec(), z[1..self.l + 1].to_vec()) + #[inline] + pub fn num_constraints(&self) -> usize { + self.A.n_rows } -} - -pub trait RelaxedR1CS: Arith { - /// returns a dummy running instance (Witness and CommittedInstance) for the current R1CS structure - fn dummy_running_instance(&self) -> (W, U); - - /// 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; - - /// 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) -> 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); - } - - let z = Self::extract_z(w, u); - self.check_relation(&z) + #[inline] + pub fn num_public_inputs(&self) -> usize { + self.l } - /// 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) + #[inline] + pub fn num_variables(&self) -> usize { + self.A.n_cols } - // Computes the E term, given A, B, C, z, u - fn compute_E( - A: &SparseMatrix, - B: &SparseMatrix, - C: &SparseMatrix, - z: &[C::ScalarField], - u: &C::ScalarField, - ) -> Result, Error> { - let Az = mat_vec_mul(A, z)?; - let Bz = mat_vec_mul(B, z)?; - let AzBz = hadamard(&Az, &Bz)?; + #[inline] + pub fn num_witnesses(&self) -> usize { + self.num_variables() - self.num_public_inputs() - 1 + } - let Cz = mat_vec_mul(C, z)?; - let uCz = vec_scalar_mul(&Cz, u); - vec_sub(&AzBz, &uCz) + /// returns a tuple containing (w, x) (witness and public inputs respectively) + pub fn split_z(&self, z: &[F]) -> (Vec, Vec) { + (z[self.l + 1..].to_vec(), z[1..self.l + 1].to_vec()) } +} - fn sample(&self, params: &CS::ProverParams, rng: impl RngCore) -> Result<(W, U), Error> - where - CS: CommitmentScheme; +impl From> for R1CS { + fn from(ccs: CCS) -> Self { + R1CS:: { + l: ccs.l, + A: ccs.M[0].clone(), + B: ccs.M[1].clone(), + C: ccs.M[2].clone(), + } + } } /// extracts arkworks ConstraintSystem matrices into crate::utils::vec::SparseMatrix format as R1CS @@ -173,27 +163,12 @@ pub fn extract_w_x(cs: &ConstraintSystem) -> (Vec, Vec) #[cfg(test)] pub mod tests { use super::*; - use crate::folding::nova::{CommittedInstance, Witness}; - use crate::{ - commitment::pedersen::Pedersen, - utils::vec::{ - is_zero_vec, - tests::{to_F_matrix, to_F_vec}, - }, + use crate::utils::vec::{ + is_zero_vec, + tests::{to_F_matrix, to_F_vec}, }; - use ark_pallas::{Fr, Projective}; - - #[test] - pub fn sample_relaxed_r1cs() { - let rng = rand::rngs::OsRng; - let r1cs = get_test_r1cs::(); - let (prover_params, _) = Pedersen::::setup(rng, r1cs.A.n_rows).unwrap(); - - let sampled: Result<(Witness, CommittedInstance), _> = - r1cs.sample::>(&prover_params, rng); - assert!(sampled.is_ok()); - } + use ark_pallas::Fr; pub fn get_test_r1cs() -> R1CS { // R1CS for: x^3 + x + 5 = y (example from article @@ -252,20 +227,20 @@ pub mod tests { fn test_eval_r1cs_relation() { let mut rng = ark_std::test_rng(); let r1cs = get_test_r1cs::(); - let mut z = get_test_z::(rng.gen::() as usize); + let (_, x, mut w) = get_test_z_split::(rng.gen::() as usize); - let f_w = r1cs.eval_relation(&z).unwrap(); + let f_w = r1cs.eval_relation(&w, &x).unwrap(); assert!(is_zero_vec(&f_w)); - z[1] = Fr::from(111); - let f_w = r1cs.eval_relation(&z).unwrap(); + w[1] = Fr::from(111); + let f_w = r1cs.eval_relation(&w, &x).unwrap(); assert!(!is_zero_vec(&f_w)); } #[test] fn test_check_r1cs_relation() { let r1cs = get_test_r1cs::(); - let z = get_test_z(5); - r1cs.check_relation(&z).unwrap(); + let (_, x, w) = get_test_z_split(5); + r1cs.check_relation(&w, &x).unwrap(); } } diff --git a/folding-schemes/src/folding/hypernova/cccs.rs b/folding-schemes/src/folding/hypernova/cccs.rs index 0a253a8..5378a63 100644 --- a/folding-schemes/src/folding/hypernova/cccs.rs +++ b/folding-schemes/src/folding/hypernova/cccs.rs @@ -3,20 +3,17 @@ use ark_ec::CurveGroup; use ark_ff::PrimeField; use ark_serialize::CanonicalDeserialize; use ark_serialize::CanonicalSerialize; -use ark_std::One; -use ark_std::Zero; -use std::sync::Arc; - -use ark_std::rand::Rng; +use ark_std::{rand::Rng, sync::Arc, One, Zero}; use super::circuits::CCCSVar; use super::Witness; use crate::arith::{ccs::CCS, Arith}; use crate::commitment::CommitmentScheme; -use crate::folding::traits::CommittedInstanceOps; +use crate::folding::circuits::CF1; +use crate::folding::traits::{CommittedInstanceOps, Dummy}; use crate::transcript::AbsorbNonNative; use crate::utils::mle::dense_vec_to_dense_mle; -use crate::utils::vec::mat_vec_mul; +use crate::utils::vec::{is_zero_vec, mat_vec_mul}; use crate::utils::virtual_polynomial::{build_eq_x_r_vec, VirtualPolynomial}; use crate::Error; @@ -93,34 +90,34 @@ impl CCS { } } -impl CCCS { - pub fn dummy(l: usize) -> CCCS - where - C::ScalarField: PrimeField, - { - CCCS:: { +impl Dummy<&CCS>> for CCCS { + fn dummy(ccs: &CCS>) -> Self { + Self { C: C::zero(), - x: vec![C::ScalarField::zero(); l], + x: vec![CF1::::zero(); ccs.l], } } +} + +impl Arith>, CCCS> for CCS> { + type Evaluation = Vec>; + + fn eval_relation(&self, w: &Witness>, u: &CCCS) -> Result { + // evaluate CCCS relation + self.eval_at_z(&[&[CF1::::one()][..], &u.x, &w.w].concat()) + } /// Perform the check of the CCCS instance described at section 4.1, /// notice that this method does not check the commitment correctness - pub fn check_relation( - &self, - ccs: &CCS, - w: &Witness, + fn check_evaluation( + _w: &Witness>, + _u: &CCCS, + e: Self::Evaluation, ) -> Result<(), Error> { - // check CCCS relation - let z: Vec = - [vec![C::ScalarField::one()], self.x.clone(), w.w.to_vec()].concat(); - // A CCCS relation is satisfied if the q(x) multivariate polynomial evaluates to zero in // the hypercube, evaluating over the whole boolean hypercube for a normal-sized instance // would take too much, this checks the CCS relation of the CCCS. - ccs.check_relation(&z)?; - - Ok(()) + is_zero_vec(&e).then_some(()).ok_or(Error::NotSatisfied) } } @@ -193,7 +190,8 @@ pub mod tests { let ccs: CCS = get_test_ccs(); let z = get_test_z(3); - ccs.check_relation(&z).unwrap(); + let (w, x) = ccs.split_z(&z); + ccs.check_relation(&w, &x).unwrap(); let beta: Vec = (0..ccs.s).map(|_| Fr::rand(&mut rng)).collect(); @@ -227,7 +225,8 @@ pub mod tests { let ccs: CCS = get_test_ccs(); let z = get_test_z(3); - ccs.check_relation(&z).unwrap(); + let (w, x) = ccs.split_z(&z); + ccs.check_relation(&w, &x).unwrap(); // Now test that if we create Q(x) with eq(d,y) where d is inside the hypercube, \sum Q(x) should be G(d) which // should be equal to q(d), since G(x) interpolates q(x) inside the hypercube diff --git a/folding-schemes/src/folding/hypernova/circuits.rs b/folding-schemes/src/folding/hypernova/circuits.rs index 1fb4f09..e9f42d8 100644 --- a/folding-schemes/src/folding/hypernova/circuits.rs +++ b/folding-schemes/src/folding/hypernova/circuits.rs @@ -42,7 +42,7 @@ use crate::folding::{ CF1, CF2, }, nova::get_r1cs_from_cs, - traits::CommittedInstanceVarOps, + traits::{CommittedInstanceVarOps, Dummy}, }; use crate::frontend::FCircuit; use crate::utils::virtual_polynomial::VPAuxInfo; @@ -602,13 +602,13 @@ where /// feed in as parameter for the AugmentedFCircuit::empty method to avoid computing them there. pub fn upper_bound_ccs(&self) -> Result, Error> { let r1cs = get_r1cs_from_cs::>(self.clone()).unwrap(); - let mut ccs = CCS::from_r1cs(r1cs.clone()); + let mut ccs = CCS::from(r1cs); let z_0 = vec![C1::ScalarField::zero(); self.F.state_len()]; let mut W_i = Witness::::dummy(&ccs); - let mut U_i = LCCCS::::dummy(ccs.l, ccs.t, ccs.s); + let mut U_i = LCCCS::::dummy(&ccs); let mut w_i = W_i.clone(); - let mut u_i = CCCS::::dummy(ccs.l); + let mut u_i = CCCS::::dummy(&ccs); let n_iters = 2; for _ in 0..n_iters { @@ -674,7 +674,7 @@ where r_w: C1::ScalarField::one(), }; W_i = Witness::::dummy(&ccs); - U_i = LCCCS::::dummy(ccs.l, ccs.t, ccs.s); + U_i = LCCCS::::dummy(&ccs); } Ok(ccs) @@ -691,7 +691,7 @@ where cs.finalize(); let cs = cs.into_inner().ok_or(Error::NoInnerConstraintSystem)?; let r1cs = extract_r1cs::(&cs); - let ccs = CCS::from_r1cs(r1cs.clone()); + let ccs = CCS::from(r1cs); Ok((cs, ccs)) } @@ -734,8 +734,8 @@ where .unwrap_or(vec![CF1::::zero(); self.F.external_inputs_len()])) })?; - let U_dummy = LCCCS::::dummy(self.ccs.l, self.ccs.t, self.ccs.s); - let u_dummy = CCCS::::dummy(self.ccs.l); + let U_dummy = LCCCS::::dummy(&self.ccs); + let u_dummy = CCCS::::dummy(&self.ccs); let U_i = LCCCSVar::::new_witness(cs.clone(), || Ok(self.U_i.unwrap_or(U_dummy.clone())))?; @@ -748,7 +748,7 @@ where let U_i1_C = NonNativeAffineVar::new_witness(cs.clone(), || { Ok(self.U_i1_C.unwrap_or_else(C1::zero)) })?; - let nimfs_proof_dummy = NIMFSProof::::dummy(&self.ccs, MU, NU); + let nimfs_proof_dummy = NIMFSProof::::dummy((&self.ccs, MU, NU)); let nimfs_proof = ProofVar::::new_witness(cs.clone(), || { Ok(self.nimfs_proof.unwrap_or(nimfs_proof_dummy)) })?; @@ -906,7 +906,8 @@ mod tests { use crate::{ arith::{ ccs::tests::{get_test_ccs, get_test_z}, - r1cs::{extract_w_x, RelaxedR1CS}, + r1cs::extract_w_x, + Arith, }, commitment::{pedersen::Pedersen, CommitmentScheme}, folding::{ @@ -1100,7 +1101,7 @@ mod tests { assert_eq!(folded_lcccs, folded_lcccs_v); // Check that the folded LCCCS instance is a valid instance with respect to the folded witness - folded_lcccs.check_relation(&ccs, &folded_witness).unwrap(); + ccs.check_relation(&folded_witness, &folded_lcccs).unwrap(); // allocate circuit inputs let cs = ConstraintSystem::::new_ref(); @@ -1248,13 +1249,13 @@ mod tests { // prepare the dummy instances let W_dummy = Witness::::dummy(&ccs); - let U_dummy = LCCCS::::dummy(ccs.l, ccs.t, ccs.s); + let U_dummy = LCCCS::::dummy(&ccs); let w_dummy = W_dummy.clone(); - let u_dummy = CCCS::::dummy(ccs.l); + let u_dummy = CCCS::::dummy(&ccs); let (cf_W_dummy, cf_U_dummy): ( CycleFoldWitness, CycleFoldCommittedInstance, - ) = cf_r1cs.dummy_running_instance(); + ) = cf_r1cs.dummy_witness_instance(); // set the initial dummy instances let mut W_i = W_dummy.clone(); @@ -1289,7 +1290,7 @@ mod tests { if i == 0 { W_i1 = Witness::::dummy(&ccs); - U_i1 = LCCCS::dummy(ccs.l, ccs.t, ccs.s); + U_i1 = LCCCS::dummy(&ccs); let u_i1_x = U_i1.hash(&sponge, pp_hash, Fr::one(), &z_0, &z_i1); @@ -1346,7 +1347,7 @@ mod tests { .unwrap(); // sanity check: check the folded instance relation - U_i1.check_relation(&ccs, &W_i1).unwrap(); + ccs.check_relation(&W_i1, &U_i1).unwrap(); let u_i1_x = U_i1.hash(&sponge, pp_hash, iFr + Fr::one(), &z_0, &z_i1); @@ -1465,7 +1466,7 @@ mod tests { (u_i, w_i) = ccs .to_cccs::<_, _, Pedersen, false>(&mut rng, &pedersen_params, &r1cs_z) .unwrap(); - u_i.check_relation(&ccs, &w_i).unwrap(); + ccs.check_relation(&w_i, &u_i).unwrap(); // sanity checks assert_eq!(w_i.w, r1cs_w_i1); @@ -1486,12 +1487,12 @@ mod tests { W_i = W_i1.clone(); // check the new LCCCS instance relation - U_i.check_relation(&ccs, &W_i).unwrap(); + ccs.check_relation(&W_i, &U_i).unwrap(); // check the new CCCS instance relation - u_i.check_relation(&ccs, &w_i).unwrap(); + ccs.check_relation(&w_i, &u_i).unwrap(); // check the CycleFold instance relation - cf_r1cs.check_relaxed_relation(&cf_W_i, &cf_U_i).unwrap(); + cf_r1cs.check_relation(&cf_W_i, &cf_U_i).unwrap(); println!("augmented_f_circuit step {}: {:?}", i, start.elapsed()); } diff --git a/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs b/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs index 5f367fb..6bff8ba 100644 --- a/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs @@ -38,8 +38,10 @@ use crate::utils::{ vec::poly_from_vec, }; use crate::Error; -use crate::{arith::ccs::CCS, folding::traits::CommittedInstanceVarOps}; -use crate::{arith::r1cs::R1CS, folding::traits::WitnessVarOps}; +use crate::{ + arith::{ccs::CCS, r1cs::R1CS}, + folding::traits::{CommittedInstanceVarOps, Dummy, WitnessVarOps}, +}; /// In-circuit representation of the Witness associated to the CommittedInstance. #[derive(Debug, Clone)] @@ -292,8 +294,8 @@ where Ok(self.z_i.unwrap_or(vec![CF1::::zero()])) })?; - let U_dummy_native = LCCCS::::dummy(self.ccs.l, self.ccs.t, self.ccs.s); - let u_dummy_native = CCCS::::dummy(self.ccs.l); + let U_dummy_native = LCCCS::::dummy(&self.ccs); + let u_dummy_native = CCCS::::dummy(&self.ccs); let w_dummy_native = Witness::::new( vec![C1::ScalarField::zero(); self.ccs.n - 3 /* (3=2+1, since u_i.x.len=2) */], ); @@ -311,7 +313,7 @@ where let W_i1 = WitnessVar::::new_witness(cs.clone(), || { Ok(self.W_i1.unwrap_or(w_dummy_native.clone())) })?; - let nimfs_proof_dummy = NIMFSProof::::dummy(&self.ccs, 1, 1); // mu=1 & nu=1 because the last fold is 2-to-1 + let nimfs_proof_dummy = NIMFSProof::::dummy((&self.ccs, 1, 1)); // mu=1 & nu=1 because the last fold is 2-to-1 let nimfs_proof = NIMFSProofVar::::new_witness(cs.clone(), || { Ok(self.nimfs_proof.unwrap_or(nimfs_proof_dummy)) })?; @@ -373,10 +375,7 @@ where let cf_u_dummy_native = CycleFoldCommittedInstance::::dummy(NovaCycleFoldConfig::::IO_LEN); - let cf_w_dummy_native = CycleFoldWitness::::dummy( - self.cf_r1cs.A.n_cols - 1 - self.cf_r1cs.l, - self.cf_E_len, - ); + let cf_w_dummy_native = CycleFoldWitness::::dummy(&self.cf_r1cs); let cf_U_i = CycleFoldCommittedInstanceVar::::new_witness(cs.clone(), || { Ok(self.cf_U_i.unwrap_or_else(|| cf_u_dummy_native.clone())) })?; @@ -527,7 +526,7 @@ pub mod tests { let n_rows = 2_u32.pow(5) as usize; let n_cols = 2_u32.pow(5) as usize; let r1cs = R1CS::::rand(&mut rng, n_rows, n_cols); - let ccs = CCS::from_r1cs(r1cs); + let ccs = CCS::from(r1cs); let z: Vec = (0..n_cols).map(|_| Fr::rand(&mut rng)).collect(); let (pedersen_params, _) = diff --git a/folding-schemes/src/folding/hypernova/lcccs.rs b/folding-schemes/src/folding/hypernova/lcccs.rs index e95fd8c..b3f6f1a 100644 --- a/folding-schemes/src/folding/hypernova/lcccs.rs +++ b/folding-schemes/src/folding/hypernova/lcccs.rs @@ -11,8 +11,10 @@ use ark_std::Zero; use super::circuits::LCCCSVar; use super::Witness; use crate::arith::ccs::CCS; +use crate::arith::Arith; use crate::commitment::CommitmentScheme; -use crate::folding::traits::CommittedInstanceOps; +use crate::folding::circuits::CF1; +use crate::folding::traits::{CommittedInstanceOps, Dummy}; use crate::transcript::AbsorbNonNative; use crate::utils::mle::dense_vec_to_dense_mle; use crate::utils::vec::mat_vec_mul; @@ -80,42 +82,41 @@ impl CCS { } } -impl LCCCS { - pub fn dummy(l: usize, t: usize, s: usize) -> LCCCS - where - C::ScalarField: PrimeField, - { - LCCCS:: { +impl Dummy<&CCS>> for LCCCS { + fn dummy(ccs: &CCS>) -> Self { + Self { C: C::zero(), - u: C::ScalarField::zero(), - x: vec![C::ScalarField::zero(); l], - r_x: vec![C::ScalarField::zero(); s], - v: vec![C::ScalarField::zero(); t], + u: CF1::::zero(), + x: vec![CF1::::zero(); ccs.l], + r_x: vec![CF1::::zero(); ccs.s], + v: vec![CF1::::zero(); ccs.t], } } +} + +impl Arith>, LCCCS> for CCS> { + type Evaluation = Vec>; /// Perform the check of the LCCCS instance described at section 4.2, /// notice that this method does not check the commitment correctness - pub fn check_relation( - &self, - ccs: &CCS, - w: &Witness, - ) -> Result<(), Error> { - // check CCS relation - let z: Vec = [vec![self.u], self.x.clone(), w.w.to_vec()].concat(); + fn eval_relation(&self, w: &Witness>, u: &LCCCS) -> Result { + let z = [&[u.u][..], &u.x, &w.w].concat(); - let computed_v: Vec = ccs - .M + self.M .iter() .map(|M_j| { - let Mz_mle = dense_vec_to_dense_mle(ccs.s, &mat_vec_mul(M_j, &z)?); - Mz_mle.evaluate(&self.r_x).ok_or(Error::EvaluationFail) + let Mz_mle = dense_vec_to_dense_mle(self.s, &mat_vec_mul(M_j, &z)?); + Mz_mle.evaluate(&u.r_x).ok_or(Error::EvaluationFail) }) - .collect::>()?; - if computed_v != self.v { - return Err(Error::NotSatisfied); - } - Ok(()) + .collect() + } + + fn check_evaluation( + _w: &Witness>, + u: &LCCCS, + e: Self::Evaluation, + ) -> Result<(), Error> { + (u.v == e).then_some(()).ok_or(Error::NotSatisfied) } } @@ -203,7 +204,7 @@ pub mod tests { let n_rows = 2_u32.pow(5) as usize; let n_cols = 2_u32.pow(5) as usize; let r1cs = R1CS::::rand(&mut rng, n_rows, n_cols); - let ccs = CCS::from_r1cs(r1cs); + let ccs = CCS::from(r1cs); let z: Vec = (0..n_cols).map(|_| Fr::rand(&mut rng)).collect(); let (pedersen_params, _) = @@ -237,12 +238,14 @@ pub mod tests { let ccs = get_test_ccs(); let z = get_test_z(3); - ccs.check_relation(&z.clone()).unwrap(); + let (w, x) = ccs.split_z(&z); + ccs.check_relation(&w, &x).unwrap(); // Mutate z so that the relation does not hold let mut bad_z = z.clone(); bad_z[3] = Fr::zero(); - assert!(ccs.check_relation(&bad_z.clone()).is_err()); + let (bad_w, bad_x) = ccs.split_z(&bad_z); + assert!(ccs.check_relation(&bad_w, &bad_x).is_err()); let (pedersen_params, _) = Pedersen::::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); diff --git a/folding-schemes/src/folding/hypernova/mod.rs b/folding-schemes/src/folding/hypernova/mod.rs index c63d943..5d570fe 100644 --- a/folding-schemes/src/folding/hypernova/mod.rs +++ b/folding-schemes/src/folding/hypernova/mod.rs @@ -24,24 +24,27 @@ use decider_eth_circuit::WitnessVar; use lcccs::LCCCS; use nimfs::NIMFS; +use crate::commitment::CommitmentScheme; use crate::constants::NOVA_N_BITS_RO; -use crate::folding::circuits::{ - cyclefold::{ - fold_cyclefold_circuit, CycleFoldCircuit, CycleFoldCommittedInstance, CycleFoldConfig, - CycleFoldWitness, +use crate::folding::{ + circuits::{ + cyclefold::{ + fold_cyclefold_circuit, CycleFoldCircuit, CycleFoldCommittedInstance, CycleFoldConfig, + CycleFoldWitness, + }, + CF2, }, - CF2, + nova::{get_r1cs_from_cs, PreprocessorParam}, + traits::{CommittedInstanceOps, Dummy, WitnessOps}, }; -use crate::folding::nova::{get_r1cs_from_cs, PreprocessorParam}; -use crate::folding::traits::{CommittedInstanceOps, WitnessOps}; use crate::frontend::FCircuit; use crate::utils::{get_cm_coordinates, pp_hash}; use crate::Error; -use crate::{arith::r1cs::RelaxedR1CS, commitment::CommitmentScheme}; use crate::{ arith::{ ccs::CCS, r1cs::{extract_w_x, R1CS}, + Arith, }, FoldingScheme, MultiFolding, }; @@ -78,8 +81,11 @@ impl Witness { // always. Self { w, r_w: F::zero() } } - pub fn dummy(ccs: &CCS) -> Self { - Witness::::new(vec![F::zero(); ccs.n - ccs.l - 1]) +} + +impl Dummy<&CCS> for Witness { + fn dummy(ccs: &CCS) -> Self { + Self::new(vec![F::zero(); ccs.n - ccs.l - 1]) } } @@ -251,7 +257,7 @@ where .to_lcccs::<_, _, CS1, H>(&mut rng, &self.cs_pp, &r1cs_z)?; #[cfg(test)] - U_i.check_relation(&self.ccs, &W_i)?; + self.ccs.check_relation(&W_i, &U_i)?; Ok((U_i, W_i)) } @@ -273,7 +279,7 @@ where .to_cccs::<_, _, CS1, H>(&mut rng, &self.cs_pp, &r1cs_z)?; #[cfg(test)] - u_i.check_relation(&self.ccs, &w_i)?; + self.ccs.check_relation(&w_i, &u_i)?; Ok((u_i, w_i)) } @@ -305,10 +311,10 @@ where external_inputs: Vec, ) -> Result, Error> { // prepare the initial dummy instances - let U_i = LCCCS::::dummy(self.ccs.l, self.ccs.t, self.ccs.s); - let mut u_i = CCCS::::dummy(self.ccs.l); + let U_i = LCCCS::::dummy(&self.ccs); + let mut u_i = CCCS::::dummy(&self.ccs); let (_, cf_U_i): (CycleFoldWitness, CycleFoldCommittedInstance) = - self.cf_r1cs.dummy_running_instance(); + self.cf_r1cs.dummy_witness_instance(); let sponge = PoseidonSponge::::new(&self.poseidon_config); @@ -329,7 +335,7 @@ where .step_native(0, state.clone(), external_inputs.clone())?; // compute u_{i+1}.x - let U_i1 = LCCCS::dummy(self.ccs.l, self.ccs.t, self.ccs.s); + let U_i1 = LCCCS::dummy(&self.ccs); let u_i1_x = U_i1.hash( &sponge, self.pp_hash, @@ -498,11 +504,11 @@ where // setup the dummy instances let W_dummy = Witness::::dummy(&ccs); - let U_dummy = LCCCS::::dummy(ccs.l, ccs.t, ccs.s); + let U_dummy = LCCCS::::dummy(&ccs); let w_dummy = W_dummy.clone(); - let mut u_dummy = CCCS::::dummy(ccs.l); + let mut u_dummy = CCCS::::dummy(&ccs); let (cf_W_dummy, cf_U_dummy): (CycleFoldWitness, CycleFoldCommittedInstance) = - cf_r1cs.dummy_running_instance(); + cf_r1cs.dummy_witness_instance(); u_dummy.x = vec![ U_dummy.hash(&sponge, pp_hash, C1::ScalarField::zero(), &z_0, &z_0), cf_U_dummy.hash_cyclefold(&sponge, pp_hash), @@ -641,7 +647,7 @@ where if self.i == C1::ScalarField::zero() { W_i1 = Witness::::dummy(&self.ccs); W_i1.r_w = self.W_i.r_w; - U_i1 = LCCCS::dummy(self.ccs.l, self.ccs.t, self.ccs.s); + U_i1 = LCCCS::dummy(&self.ccs); let u_i1_x = U_i1.hash( &sponge, @@ -697,7 +703,7 @@ where // sanity check: check the folded instance relation #[cfg(test)] - U_i1.check_relation(&self.ccs, &W_i1)?; + self.ccs.check_relation(&W_i1, &U_i1)?; let u_i1_x = U_i1.hash( &sponge, @@ -831,9 +837,9 @@ where #[cfg(test)] { // check the new LCCCS instance relation - self.U_i.check_relation(&self.ccs, &self.W_i)?; + self.ccs.check_relation(&self.W_i, &self.U_i)?; // check the new CCCS instance relation - self.u_i.check_relation(&self.ccs, &self.w_i)?; + self.ccs.check_relation(&self.w_i, &self.u_i)?; } Ok(()) @@ -899,12 +905,12 @@ where } // check LCCCS satisfiability - U_i.check_relation(&vp.ccs, &W_i)?; + vp.ccs.check_relation(&W_i, &U_i)?; // check CCCS satisfiability - u_i.check_relation(&vp.ccs, &w_i)?; + vp.ccs.check_relation(&w_i, &u_i)?; // check CycleFold's RelaxedR1CS satisfiability - vp.cf_r1cs.check_relaxed_relation(&cf_W_i, &cf_U_i)?; + vp.cf_r1cs.check_relation(&cf_W_i, &cf_U_i)?; Ok(()) } diff --git a/folding-schemes/src/folding/hypernova/nimfs.rs b/folding-schemes/src/folding/hypernova/nimfs.rs index 6312162..1fae370 100644 --- a/folding-schemes/src/folding/hypernova/nimfs.rs +++ b/folding-schemes/src/folding/hypernova/nimfs.rs @@ -13,6 +13,8 @@ use super::{ }; use crate::arith::ccs::CCS; use crate::constants::NOVA_N_BITS_RO; +use crate::folding::circuits::CF1; +use crate::folding::traits::Dummy; use crate::transcript::Transcript; use crate::utils::sum_check::structs::{IOPProof as SumCheckProof, IOPProverMessage}; use crate::utils::sum_check::{IOPSumCheck, SumCheck}; @@ -29,8 +31,8 @@ pub struct NIMFSProof { pub sigmas_thetas: SigmasThetas, } -impl NIMFSProof { - pub fn dummy(ccs: &CCS, mu: usize, nu: usize) -> Self { +impl Dummy<(&CCS>, usize, usize)> for NIMFSProof { + fn dummy((ccs, mu, nu): (&CCS>, usize, usize)) -> Self { // use 'C::ScalarField::one()' instead of 'zero()' to enforce the NIMFSProof to have the // same in-circuit representation to match the number of constraints of an actual proof. NIMFSProof:: { @@ -410,8 +412,10 @@ pub mod tests { let ccs = get_test_ccs(); let z1 = get_test_z::(3); let z2 = get_test_z::(4); - ccs.check_relation(&z1).unwrap(); - ccs.check_relation(&z2).unwrap(); + let (w1, x1) = ccs.split_z(&z1); + let (w2, x2) = ccs.split_z(&z2); + ccs.check_relation(&w1, &x1).unwrap(); + ccs.check_relation(&w2, &x2).unwrap(); let mut rng = test_rng(); let r_x_prime: Vec = (0..ccs.s).map(|_| Fr::rand(&mut rng)).collect(); @@ -429,8 +433,8 @@ pub mod tests { .to_cccs::<_, Projective, Pedersen, false>(&mut rng, &pedersen_params, &z2) .unwrap(); - lcccs.check_relation(&ccs, &w1).unwrap(); - cccs.check_relation(&ccs, &w2).unwrap(); + ccs.check_relation(&w1, &lcccs).unwrap(); + ccs.check_relation(&w2, &cccs).unwrap(); let mut rng = test_rng(); let rho = Fr::rand(&mut rng); @@ -446,7 +450,7 @@ pub mod tests { let w_folded = NIMFS::>::fold_witness(&[w1], &[w2], rho); // check lcccs relation - folded.check_relation(&ccs, &w_folded).unwrap(); + ccs.check_relation(&w_folded, &folded).unwrap(); } /// Perform multifolding of an LCCCS instance with a CCCS instance (as described in the paper) @@ -506,7 +510,7 @@ pub mod tests { assert_eq!(folded_lcccs, folded_lcccs_v); // Check that the folded LCCCS instance is a valid instance with respect to the folded witness - folded_lcccs.check_relation(&ccs, &folded_witness).unwrap(); + ccs.check_relation(&folded_witness, &folded_lcccs).unwrap(); } /// Perform multiple steps of multifolding of an LCCCS instance with a CCCS instance @@ -566,7 +570,7 @@ pub mod tests { assert_eq!(folded_lcccs, folded_lcccs_v); // check that the folded instance with the folded witness holds the LCCCS relation - folded_lcccs.check_relation(&ccs, &folded_witness).unwrap(); + ccs.check_relation(&folded_witness, &folded_lcccs).unwrap(); running_instance = folded_lcccs; w1 = folded_witness; @@ -652,7 +656,7 @@ pub mod tests { assert_eq!(folded_lcccs, folded_lcccs_v); // Check that the folded LCCCS instance is a valid instance with respect to the folded witness - folded_lcccs.check_relation(&ccs, &folded_witness).unwrap(); + ccs.check_relation(&folded_witness, &folded_lcccs).unwrap(); } /// Test that generates mu>1 and nu>1 instances, and folds them in a single multifolding step @@ -740,7 +744,7 @@ pub mod tests { assert_eq!(folded_lcccs, folded_lcccs_v); // Check that the folded LCCCS instance is a valid instance with respect to the folded witness - folded_lcccs.check_relation(&ccs, &folded_witness).unwrap(); + ccs.check_relation(&folded_witness, &folded_lcccs).unwrap(); } } } diff --git a/folding-schemes/src/folding/hypernova/utils.rs b/folding-schemes/src/folding/hypernova/utils.rs index 8e0b084..b2df214 100644 --- a/folding-schemes/src/folding/hypernova/utils.rs +++ b/folding-schemes/src/folding/hypernova/utils.rs @@ -230,8 +230,10 @@ pub mod tests { let ccs = get_test_ccs(); let z1 = get_test_z(3); let z2 = get_test_z(4); - ccs.check_relation(&z1).unwrap(); - ccs.check_relation(&z2).unwrap(); + let (w1, x1) = ccs.split_z(&z1); + let (w2, x2) = ccs.split_z(&z2); + ccs.check_relation(&w1, &x1).unwrap(); + ccs.check_relation(&w2, &x2).unwrap(); let mut rng = test_rng(); let gamma: Fr = Fr::rand(&mut rng); @@ -282,8 +284,10 @@ pub mod tests { let ccs: CCS = get_test_ccs(); let z1 = get_test_z(3); let z2 = get_test_z(4); - ccs.check_relation(&z1).unwrap(); - ccs.check_relation(&z2).unwrap(); + let (w1, x1) = ccs.split_z(&z1); + let (w2, x2) = ccs.split_z(&z2); + ccs.check_relation(&w1, &x1).unwrap(); + ccs.check_relation(&w2, &x2).unwrap(); let gamma: Fr = Fr::rand(&mut rng); let beta: Vec = (0..ccs.s).map(|_| Fr::rand(&mut rng)).collect(); diff --git a/folding-schemes/src/folding/nova/circuits.rs b/folding-schemes/src/folding/nova/circuits.rs index 6e90460..cd484ab 100644 --- a/folding-schemes/src/folding/nova/circuits.rs +++ b/folding-schemes/src/folding/nova/circuits.rs @@ -31,7 +31,10 @@ use crate::folding::circuits::{ }; use crate::frontend::FCircuit; use crate::transcript::{AbsorbNonNativeGadget, Transcript, TranscriptVar}; -use crate::{constants::NOVA_N_BITS_RO, folding::traits::CommittedInstanceVarOps}; +use crate::{ + constants::NOVA_N_BITS_RO, + folding::traits::{CommittedInstanceVarOps, Dummy}, +}; /// CommittedInstanceVar contains the u, x, cmE and cmW values which are folded on the main Nova /// constraints field (E1::Fr, where E1 is the main curve). The peculiarity is that cmE and cmW are diff --git a/folding-schemes/src/folding/nova/decider_circuits.rs b/folding-schemes/src/folding/nova/decider_circuits.rs index 31d7d60..959a1e3 100644 --- a/folding-schemes/src/folding/nova/decider_circuits.rs +++ b/folding-schemes/src/folding/nova/decider_circuits.rs @@ -40,7 +40,7 @@ use crate::folding::circuits::{ CF1, CF2, }; use crate::folding::nova::NovaCycleFoldConfig; -use crate::folding::traits::CommittedInstanceVarOps; +use crate::folding::traits::{CommittedInstanceVarOps, Dummy}; use crate::frontend::FCircuit; use crate::utils::vec::poly_from_vec; use crate::Error; @@ -214,11 +214,8 @@ where Ok(self.z_i.unwrap_or(vec![CF1::::zero()])) })?; - let u_dummy_native = CommittedInstance::::dummy(2); - let w_dummy_native = Witness::::dummy( - self.r1cs.A.n_cols - 3, /* (3=2+1, since u_i.x.len=2) */ - self.E_len, - ); + let u_dummy_native = CommittedInstance::::dummy(&self.r1cs); + let w_dummy_native = Witness::::dummy(&self.r1cs); let u_i = CommittedInstanceVar::::new_witness(cs.clone(), || { Ok(self.u_i.unwrap_or(u_dummy_native.clone())) @@ -436,9 +433,8 @@ where Ok(self.pp_hash.unwrap_or_else(CF1::::zero)) })?; - let cf_u_dummy_native = CommittedInstance::::dummy(NovaCycleFoldConfig::::IO_LEN); - let w_dummy_native = - Witness::::dummy(self.cf_r1cs.A.n_cols - 1 - self.cf_r1cs.l, self.cf_E_len); + let cf_u_dummy_native = CommittedInstance::::dummy(&self.cf_r1cs); + let w_dummy_native = Witness::::dummy(&self.cf_r1cs); let cf_U_i = CommittedInstanceVar::::new_input(cs.clone(), || { Ok(self.cf_U_i.unwrap_or_else(|| cf_u_dummy_native.clone())) })?; diff --git a/folding-schemes/src/folding/nova/decider_eth_circuit.rs b/folding-schemes/src/folding/nova/decider_eth_circuit.rs index 049ed25..7711386 100644 --- a/folding-schemes/src/folding/nova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/nova/decider_eth_circuit.rs @@ -29,6 +29,7 @@ use super::{ nifs::NIFS, CommittedInstance, Nova, Witness, }; +use crate::commitment::{pedersen::Params as PedersenParams, CommitmentScheme}; use crate::folding::circuits::{ cyclefold::{CycleFoldCommittedInstance, CycleFoldWitness}, nonnative::{affine::NonNativeAffineVar, uint::NonNativeUintVar}, @@ -41,10 +42,9 @@ use crate::utils::{ vec::poly_from_vec, }; use crate::Error; -use crate::{arith::r1cs::R1CS, folding::traits::WitnessVarOps}; use crate::{ - commitment::{pedersen::Params as PedersenParams, CommitmentScheme}, - folding::traits::CommittedInstanceVarOps, + arith::r1cs::R1CS, + folding::traits::{CommittedInstanceVarOps, Dummy, WitnessVarOps}, }; #[derive(Debug, Clone)] @@ -356,11 +356,8 @@ where Ok(self.z_i.unwrap_or(vec![CF1::::zero()])) })?; - let u_dummy_native = CommittedInstance::::dummy(2); - let w_dummy_native = Witness::::dummy( - self.r1cs.A.n_cols - 3, /* (3=2+1, since u_i.x.len=2) */ - self.E_len, - ); + let u_dummy_native = CommittedInstance::::dummy(&self.r1cs); + let w_dummy_native = Witness::::dummy(&self.r1cs); let u_i = CommittedInstanceVar::::new_witness(cs.clone(), || { Ok(self.u_i.unwrap_or(u_dummy_native.clone())) @@ -437,10 +434,7 @@ where let cf_u_dummy_native = CycleFoldCommittedInstance::::dummy(NovaCycleFoldConfig::::IO_LEN); - let w_dummy_native = CycleFoldWitness::::dummy( - self.cf_r1cs.A.n_cols - 1 - self.cf_r1cs.l, - self.cf_E_len, - ); + let w_dummy_native = CycleFoldWitness::::dummy(&self.cf_r1cs); let cf_U_i = CycleFoldCommittedInstanceVar::::new_witness(cs.clone(), || { Ok(self.cf_U_i.unwrap_or_else(|| cf_u_dummy_native.clone())) })?; @@ -608,7 +602,6 @@ pub mod tests { r1cs::{ extract_r1cs, extract_w_x, tests::{get_test_r1cs, get_test_z}, - RelaxedR1CS, }, Arith, }; @@ -618,20 +611,18 @@ pub mod tests { use crate::transcript::poseidon::poseidon_canonical_config; use crate::FoldingScheme; - fn prepare_instances, R: Rng>( + // Convert `z` to a witness-instance pair for the relaxed R1CS + fn prepare_relaxed_witness_instance, R: Rng>( mut rng: R, r1cs: &R1CS, z: &[C::ScalarField], - ) -> (Witness, CommittedInstance) - where - C::ScalarField: Absorb, - { + ) -> (Witness, CommittedInstance) { let (w, x) = r1cs.split_z(z); let (cs_pp, _) = CS::setup(&mut rng, max(w.len(), r1cs.A.n_rows)).unwrap(); let mut w = Witness::new::(w, r1cs.A.n_rows, &mut rng); - w.E = r1cs.eval_relation(z).unwrap(); + w.E = r1cs.eval_at_z(z).unwrap(); let mut u = w.commit::(&cs_pp, x).unwrap(); u.u = z[0]; @@ -643,9 +634,10 @@ pub mod tests { let rng = &mut thread_rng(); let r1cs: R1CS = get_test_r1cs(); + let mut z = get_test_z(3); - z[0] = Fr::rand(rng); - let (w, u) = prepare_instances::<_, Pedersen, _>(rng, &r1cs, &z); + z[0] = Fr::rand(rng); // Randomize `z[0]` (i.e. `u.u`) to test the relaxed R1CS + let (w, u) = prepare_relaxed_witness_instance::<_, Pedersen, _>(rng, &r1cs, &z); let cs = ConstraintSystem::::new_ref(); @@ -673,12 +665,11 @@ pub mod tests { let r1cs = extract_r1cs::(&cs); let (w, x) = extract_w_x::(&cs); - let mut z = [vec![Fr::one()], x, w].concat(); - r1cs.check_relation(&z).unwrap(); + r1cs.check_relation(&w, &x).unwrap(); - z[0] = Fr::rand(rng); - let (w, u) = prepare_instances::<_, Pedersen, _>(rng, &r1cs, &z); - r1cs.check_relaxed_relation(&w, &u).unwrap(); + let z = [vec![Fr::rand(rng)], x, w].concat(); + let (w, u) = prepare_relaxed_witness_instance::<_, Pedersen, _>(rng, &r1cs, &z); + r1cs.check_relation(&w, &u).unwrap(); // set new CS for the circuit that checks the RelaxedR1CS of our original circuit let cs = ConstraintSystem::::new_ref(); @@ -767,9 +758,10 @@ pub mod tests { let cs = cs.into_inner().unwrap(); let r1cs = extract_r1cs::(&cs); let (w, x) = extract_w_x::(&cs); - let z = [vec![Fq::rand(rng)], x, w].concat(); - let (w, u) = prepare_instances::<_, Pedersen, _>(rng, &r1cs, &z); + let z = [vec![Fq::rand(rng)], x, w].concat(); + let (w, u) = + prepare_relaxed_witness_instance::<_, Pedersen, _>(rng, &r1cs, &z); // natively let cs = ConstraintSystem::::new_ref(); diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index 5863f7a..d6436c5 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -19,18 +19,21 @@ use crate::folding::circuits::cyclefold::{ fold_cyclefold_circuit, CycleFoldCircuit, CycleFoldCommittedInstance, CycleFoldConfig, CycleFoldWitness, }; -use crate::folding::circuits::CF2; +use crate::folding::{ + circuits::{CF1, CF2}, + traits::Dummy, +}; use crate::frontend::FCircuit; use crate::transcript::{poseidon::poseidon_canonical_config, AbsorbNonNative, Transcript}; use crate::utils::vec::is_zero_vec; use crate::Error; use crate::FoldingScheme; -use crate::{arith::r1cs::RelaxedR1CS, commitment::CommitmentScheme}; use crate::{ arith::r1cs::{extract_r1cs, extract_w_x, R1CS}, constants::NOVA_N_BITS_RO, utils::{get_cm_coordinates, pp_hash}, }; +use crate::{arith::Arith, commitment::CommitmentScheme}; use circuits::{AugmentedFCircuit, ChallengeGadget, CommittedInstanceVar}; use nifs::NIFS; @@ -76,17 +79,23 @@ pub struct CommittedInstance { pub x: Vec, } -impl CommittedInstance { - pub fn dummy(io_len: usize) -> Self { +impl Dummy for CommittedInstance { + fn dummy(io_len: usize) -> Self { Self { cmE: C::zero(), - u: C::ScalarField::zero(), + u: CF1::::zero(), cmW: C::zero(), - x: vec![C::ScalarField::zero(); io_len], + x: vec![CF1::::zero(); io_len], } } } +impl Dummy<&R1CS>> for CommittedInstance { + fn dummy(r1cs: &R1CS>) -> Self { + Self::dummy(r1cs.l) + } +} + impl Absorb for CommittedInstance where C::ScalarField: Absorb, @@ -149,18 +158,6 @@ impl Witness { } } - pub fn dummy(w_len: usize, e_len: usize) -> Self { - let (rW, rE) = (C::ScalarField::zero(), C::ScalarField::zero()); - let w = vec![C::ScalarField::zero(); w_len]; - - Self { - E: vec![C::ScalarField::zero(); e_len], - rE, - W: w, - rW, - } - } - pub fn commit, const HC: bool>( &self, params: &CS::ProverParams, @@ -180,6 +177,17 @@ impl Witness { } } +impl Dummy<&R1CS>> for Witness { + fn dummy(r1cs: &R1CS>) -> Self { + Self { + E: vec![C::ScalarField::zero(); r1cs.A.n_rows], + rE: C::ScalarField::zero(), + W: vec![C::ScalarField::zero(); r1cs.A.n_cols - 1 - r1cs.l], + rW: C::ScalarField::zero(), + } + } +} + impl WitnessOps for Witness { type Var = WitnessVar; @@ -562,9 +570,9 @@ where let pp_hash = vp.pp_hash()?; // setup the dummy instances - let (W_dummy, U_dummy) = r1cs.dummy_running_instance(); - let (w_dummy, u_dummy) = r1cs.dummy_incoming_instance(); - let (cf_W_dummy, cf_U_dummy) = cf_r1cs.dummy_running_instance(); + let (W_dummy, U_dummy) = r1cs.dummy_witness_instance(); + let (w_dummy, u_dummy) = r1cs.dummy_witness_instance(); + let (cf_W_dummy, cf_U_dummy) = cf_r1cs.dummy_witness_instance(); // W_dummy=W_0 is a 'dummy witness', all zeroes, but with the size corresponding to the // R1CS that we're working with. @@ -815,10 +823,11 @@ where #[cfg(test)] { - self.cf_r1cs.check_tight_relation(&_cfW_w_i, &cfW_u_i)?; - self.cf_r1cs.check_tight_relation(&_cfE_w_i, &cfE_u_i)?; - self.cf_r1cs - .check_relaxed_relation(&self.cf_W_i, &self.cf_U_i)?; + cfW_u_i.check_incoming()?; + cfE_u_i.check_incoming()?; + self.cf_r1cs.check_relation(&_cfW_w_i, &cfW_u_i)?; + self.cf_r1cs.check_relation(&_cfE_w_i, &cfE_u_i)?; + self.cf_r1cs.check_relation(&self.cf_W_i, &self.cf_U_i)?; } } @@ -850,8 +859,9 @@ where #[cfg(test)] { - self.r1cs.check_tight_relation(&self.w_i, &self.u_i)?; - self.r1cs.check_relaxed_relation(&self.W_i, &self.U_i)?; + self.u_i.check_incoming()?; + self.r1cs.check_relation(&self.w_i, &self.u_i)?; + self.r1cs.check_relation(&self.W_i, &self.U_i)?; } Ok(()) @@ -917,13 +927,15 @@ where return Err(Error::IVCVerificationFail); } - // check R1CS satisfiability, which also enforces u_i.cmE==0, u_i.u==1 - vp.r1cs.check_tight_relation(&w_i, &u_i)?; + // check R1CS satisfiability, which is equivalent to checking if `u_i` + // is an incoming instance and if `w_i` and `u_i` satisfy RelaxedR1CS + u_i.check_incoming()?; + vp.r1cs.check_relation(&w_i, &u_i)?; // check RelaxedR1CS satisfiability - vp.r1cs.check_relaxed_relation(&W_i, &U_i)?; + vp.r1cs.check_relation(&W_i, &U_i)?; // check CycleFold RelaxedR1CS satisfiability - vp.cf_r1cs.check_relaxed_relation(&cf_W_i, &cf_U_i)?; + vp.cf_r1cs.check_relation(&cf_W_i, &cf_U_i)?; Ok(()) } diff --git a/folding-schemes/src/folding/nova/nifs.rs b/folding-schemes/src/folding/nova/nifs.rs index 03a0ef3..238655b 100644 --- a/folding-schemes/src/folding/nova/nifs.rs +++ b/folding-schemes/src/folding/nova/nifs.rs @@ -210,13 +210,16 @@ pub mod tests { use ark_pallas::{Fr, Projective}; use ark_std::{ops::Mul, test_rng, UniformRand}; - use crate::arith::r1cs::{ - tests::{get_test_r1cs, get_test_z}, - RelaxedR1CS, - }; use crate::commitment::pedersen::{Params as PedersenParams, Pedersen}; use crate::folding::nova::circuits::ChallengeGadget; use crate::transcript::poseidon::poseidon_canonical_config; + use crate::{ + arith::{ + r1cs::tests::{get_test_r1cs, get_test_z}, + Arith, + }, + folding::traits::Dummy, + }; #[allow(clippy::type_complexity)] pub(crate) fn prepare_simple_fold_inputs() -> ( @@ -302,13 +305,13 @@ pub mod tests { fn test_nifs_fold_dummy() { let r1cs = get_test_r1cs::(); let z1 = get_test_z(3); - let (w1, x1) = r1cs.split_z(&z1); + let (_, x1) = r1cs.split_z(&z1); let mut rng = ark_std::test_rng(); let (pedersen_params, _) = Pedersen::::setup(&mut rng, r1cs.A.n_cols).unwrap(); // dummy instance, witness and public inputs zeroes - let w_dummy = Witness::::dummy(w1.len(), r1cs.A.n_rows); + let w_dummy = Witness::::dummy(&r1cs); let mut u_dummy = w_dummy .commit::, false>(&pedersen_params, vec![Fr::zero(); x1.len()]) .unwrap(); @@ -318,8 +321,8 @@ pub mod tests { let u_i = u_dummy.clone(); let W_i = w_dummy.clone(); let U_i = u_dummy.clone(); - r1cs.check_relaxed_relation(&w_i, &u_i).unwrap(); - r1cs.check_relaxed_relation(&W_i, &U_i).unwrap(); + r1cs.check_relation(&w_i, &u_i).unwrap(); + r1cs.check_relation(&W_i, &U_i).unwrap(); let r_Fr = Fr::from(3_u32); @@ -336,7 +339,7 @@ pub mod tests { r_Fr, &w_i, &u_i, &W_i, &U_i, &T, cmT, ) .unwrap(); - r1cs.check_relaxed_relation(&W_i1, &U_i1).unwrap(); + r1cs.check_relation(&W_i1, &U_i1).unwrap(); } // fold 2 instances into one @@ -350,9 +353,9 @@ pub mod tests { assert_eq!(ci3_v, ci3); // check that relations hold for the 2 inputted instances and the folded one - r1cs.check_relaxed_relation(&w1, &ci1).unwrap(); - r1cs.check_relaxed_relation(&w2, &ci2).unwrap(); - r1cs.check_relaxed_relation(&w3, &ci3).unwrap(); + r1cs.check_relation(&w1, &ci1).unwrap(); + r1cs.check_relation(&w2, &ci2).unwrap(); + r1cs.check_relation(&w3, &ci3).unwrap(); // check that folded commitments from folded instance (ci) are equal to folding the // use folded rE, rW to commit w3 @@ -427,7 +430,7 @@ pub mod tests { .commit::, false>(&pedersen_params, x) .unwrap(); - r1cs.check_relaxed_relation(&running_instance_w, &running_committed_instance) + r1cs.check_relation(&running_instance_w, &running_committed_instance) .unwrap(); let num_iters = 10; @@ -440,7 +443,7 @@ pub mod tests { let incoming_committed_instance = incoming_instance_w .commit::, false>(&pedersen_params, x) .unwrap(); - r1cs.check_relaxed_relation(&incoming_instance_w, &incoming_committed_instance) + r1cs.check_relation(&incoming_instance_w, &incoming_committed_instance) .unwrap(); let r = Fr::rand(&mut rng); // folding challenge would come from the RO @@ -474,7 +477,7 @@ pub mod tests { &cmT, ); - r1cs.check_relaxed_relation(&folded_w, &folded_committed_instance) + r1cs.check_relation(&folded_w, &folded_committed_instance) .unwrap(); // set running_instance for next loop iteration diff --git a/folding-schemes/src/folding/nova/traits.rs b/folding-schemes/src/folding/nova/traits.rs index 62870d9..8bf336a 100644 --- a/folding-schemes/src/folding/nova/traits.rs +++ b/folding-schemes/src/folding/nova/traits.rs @@ -1,50 +1,66 @@ use ark_ec::CurveGroup; -use ark_std::{rand::RngCore, One, UniformRand}; +use ark_std::{rand::RngCore, UniformRand}; use super::{CommittedInstance, Witness}; -use crate::arith::r1cs::{RelaxedR1CS, R1CS}; +use crate::arith::ArithSampler; +use crate::arith::{r1cs::R1CS, Arith}; +use crate::commitment::CommitmentScheme; +use crate::folding::circuits::CF1; use crate::Error; -impl RelaxedR1CS, CommittedInstance> for R1CS { - fn dummy_running_instance(&self) -> (Witness, CommittedInstance) { - let w_len = self.A.n_cols - 1 - self.l; - let w_dummy = Witness::::dummy(w_len, self.A.n_rows); - let u_dummy = CommittedInstance::::dummy(self.l); - (w_dummy, u_dummy) - } - - fn dummy_incoming_instance(&self) -> (Witness, CommittedInstance) { - self.dummy_running_instance() - } +/// Implements `Arith` for R1CS, where the witness is of type [`Witness`], and +/// the committed instance is of type [`CommittedInstance`]. +/// +/// Due to the error terms `Witness.E` and `CommittedInstance.u`, R1CS here is +/// considered as a relaxed R1CS. +/// +/// One may wonder why we do not provide distinct structs for R1CS and relaxed +/// R1CS. +/// This is because both plain R1CS and relaxed R1CS have the same structure: +/// they are both represented by three matrices. +/// What makes them different is the error terms, which are not part of the R1CS +/// struct, but are part of the witness and committed instance. +/// +/// As a follow-up, one may further ask why not providing a trait for relaxed +/// R1CS and implement it for the `R1CS` struct, where the relaxed R1CS trait +/// has methods for relaxed satisfiability check, while the `Arith` trait that +/// `R1CS` implements has methods for plain satisfiability check. +/// However, it would be more ideal if we have a single method that can smartly +/// choose the type of satisfiability check, which would make the code more +/// generic and easier to maintain. +/// +/// This is achieved thanks to the new design of the [`Arith`] trait, where we +/// can implement the trait for the same constraint system with different types +/// of witnesses and committed instances. +/// For R1CS, whether it is relaxed or not is now determined by the types of `W` +/// and `U`: the satisfiability check is relaxed if `W` and `U` are defined by +/// folding schemes, and plain if they are vectors of field elements. +impl Arith, CommittedInstance> for R1CS> { + type Evaluation = Vec>; - fn is_relaxed(_w: &Witness, u: &CommittedInstance) -> bool { - u.cmE != C::zero() || u.u != C::ScalarField::one() - } - - fn extract_z(w: &Witness, u: &CommittedInstance) -> Vec { - [&[u.u][..], &u.x, &w.W].concat() + fn eval_relation( + &self, + w: &Witness, + u: &CommittedInstance, + ) -> Result { + self.eval_at_z(&[&[u.u][..], &u.x, &w.W].concat()) } - fn check_error_terms( + fn check_evaluation( w: &Witness, _u: &CommittedInstance, - e: Vec, + e: Self::Evaluation, ) -> Result<(), Error> { - if w.E == e { - Ok(()) - } else { - Err(Error::NotSatisfied) - } + (w.E == e).then_some(()).ok_or(Error::NotSatisfied) } +} - fn sample( +impl ArithSampler, CommittedInstance> for R1CS> { + fn sample_witness_instance>( &self, params: &CS::ProverParams, mut rng: impl RngCore, - ) -> Result<(Witness, CommittedInstance), Error> - where - CS: crate::commitment::CommitmentScheme, - { + ) -> Result<(Witness, CommittedInstance), Error> { // Implements sampling a (committed) RelaxedR1CS // See construction 5 in https://eprint.iacr.org/2023/573.pdf let u = C::ScalarField::rand(&mut rng); @@ -61,16 +77,7 @@ impl RelaxedR1CS, CommittedInstance> for R1CS, CommittedInstance>>::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 - ); + let E = self.eval_at_z(&z)?; let witness = Witness { E, rE, W, rW }; let mut cm_witness = witness.commit::(params, x)?; @@ -79,7 +86,7 @@ impl RelaxedR1CS, CommittedInstance> for R1CS(&nova.cs_pp, &mut rng)?; + let (W_r, U_r) = nova + .r1cs + .sample_witness_instance::(&nova.cs_pp, &mut rng)?; // 3. Fold the instance-witness pair (U_f, W_f) with (U_r, W_r) // a. Compute T @@ -279,10 +281,10 @@ where ); // 5. Check that W^{\prime}_i is a satisfying witness - r1cs.check_relaxed_relation(&proof.W_i_prime, &U_i_prime)?; + r1cs.check_relation(&proof.W_i_prime, &U_i_prime)?; // 6. Check that the cyclefold instance-witness pair satisfies the cyclefold relaxed r1cs - cf_r1cs.check_relaxed_relation(&proof.cf_W_i, &proof.cf_U_i)?; + cf_r1cs.check_relation(&proof.cf_W_i, &proof.cf_U_i)?; Ok(()) } @@ -370,7 +372,7 @@ pub mod tests { ); let (_, sampled_committed_instance) = nova .r1cs - .sample::>(&nova.cs_pp, rng) + .sample_witness_instance::>(&nova.cs_pp, rng) .unwrap(); // proof verification fails with incorrect running instance @@ -407,7 +409,7 @@ pub mod tests { ); let (sampled_committed_witness, _) = nova .r1cs - .sample::>(&nova.cs_pp, rng) + .sample_witness_instance::>(&nova.cs_pp, rng) .unwrap(); // proof generation fails with incorrect running witness diff --git a/folding-schemes/src/folding/protogalaxy/circuits.rs b/folding-schemes/src/folding/protogalaxy/circuits.rs index 4ec2fad..4c30886 100644 --- a/folding-schemes/src/folding/protogalaxy/circuits.rs +++ b/folding-schemes/src/folding/protogalaxy/circuits.rs @@ -33,7 +33,7 @@ use crate::{ nonnative::{affine::NonNativeAffineVar, uint::NonNativeUintVar}, CF1, CF2, }, - traits::CommittedInstanceVarOps, + traits::{CommittedInstanceVarOps, Dummy}, }, frontend::FCircuit, transcript::{AbsorbNonNativeGadget, TranscriptVar}, @@ -47,13 +47,13 @@ impl FoldingGadget { pub fn fold_committed_instance( transcript: &mut impl TranscriptVar, // running instance - instance: &CommittedInstanceVar, + instance: &CommittedInstanceVar, // incoming instances - vec_instances: &[CommittedInstanceVar], + vec_instances: &[CommittedInstanceVar], // polys from P F_coeffs: Vec>, K_coeffs: Vec>, - ) -> Result<(CommittedInstanceVar, Vec>), SynthesisError> { + ) -> Result<(CommittedInstanceVar, Vec>), SynthesisError> { let t = instance.betas.len(); // absorb the committed instances @@ -135,13 +135,13 @@ impl AugmentationGadget { #[allow(clippy::type_complexity)] pub fn prepare_and_fold_primary( transcript: &mut impl TranscriptVar, S>, - U: CommittedInstanceVar, + U: CommittedInstanceVar, u_phis: Vec>, u_xs: Vec>>>, new_U_phi: NonNativeAffineVar, F_coeffs: Vec>>, K_coeffs: Vec>>, - ) -> Result<(CommittedInstanceVar, Vec>>), SynthesisError> { + ) -> Result<(CommittedInstanceVar, Vec>>), SynthesisError> { assert_eq!(u_phis.len(), u_xs.len()); // Prepare the incoming instances. @@ -250,7 +250,7 @@ pub struct AugmentedFCircuit< pub(super) external_inputs: Vec>, pub(super) F: FC, // F circuit pub(super) u_i_phi: C1, - pub(super) U_i: CommittedInstance, + pub(super) U_i: CommittedInstance, pub(super) U_i1_phi: C1, pub(super) F_coeffs: Vec>, pub(super) K_coeffs: Vec>, @@ -278,7 +278,7 @@ where d: usize, k: usize, ) -> Self { - let u_dummy = CommittedInstance::dummy_running(2, t); + let u_dummy = CommittedInstance::dummy((2, t)); let cf_u_dummy = CycleFoldCommittedInstance::dummy(ProtoGalaxyCycleFoldConfig::::IO_LEN); @@ -327,8 +327,8 @@ where let external_inputs = Vec::>>::new_witness(cs.clone(), || Ok(self.external_inputs))?; - let u_dummy = CommittedInstance::::dummy_running(2, self.U_i.betas.len()); - let U_i = CommittedInstanceVar::::new_witness(cs.clone(), || Ok(self.U_i))?; + let u_dummy = CommittedInstance::::dummy((2, self.U_i.betas.len())); + let U_i = CommittedInstanceVar::::new_witness(cs.clone(), || Ok(self.U_i))?; let u_i_phi = NonNativeAffineVar::new_witness(cs.clone(), || Ok(self.u_i_phi))?; let U_i1_phi = NonNativeAffineVar::new_witness(cs.clone(), || Ok(self.U_i1_phi))?; let phi_stars = diff --git a/folding-schemes/src/folding/protogalaxy/constants.rs b/folding-schemes/src/folding/protogalaxy/constants.rs new file mode 100644 index 0000000..cadbf10 --- /dev/null +++ b/folding-schemes/src/folding/protogalaxy/constants.rs @@ -0,0 +1,4 @@ +/// `RUNNING` indicates that the committed instance is a running instance. +pub const RUNNING: bool = true; +/// `INCOMING` indicates that the committed instance is an incoming instance. +pub const INCOMING: bool = false; diff --git a/folding-schemes/src/folding/protogalaxy/folding.rs b/folding-schemes/src/folding/protogalaxy/folding.rs index 71add9a..3aefee4 100644 --- a/folding-schemes/src/folding/protogalaxy/folding.rs +++ b/folding-schemes/src/folding/protogalaxy/folding.rs @@ -14,9 +14,7 @@ use super::utils::{all_powers, betas_star, exponential_powers, pow_i}; use super::ProtoGalaxyError; use super::{CommittedInstance, Witness}; -#[cfg(test)] -use crate::arith::r1cs::RelaxedR1CS; -use crate::arith::{r1cs::R1CS, Arith}; +use crate::arith::r1cs::R1CS; use crate::transcript::Transcript; use crate::utils::vec::*; use crate::Error; @@ -38,14 +36,14 @@ where transcript: &mut impl Transcript, r1cs: &R1CS, // running instance - instance: &CommittedInstance, + instance: &CommittedInstance, w: &Witness, // incoming instances - vec_instances: &[CommittedInstance], + vec_instances: &[CommittedInstance], vec_w: &[Witness], ) -> Result< ( - CommittedInstance, + CommittedInstance, Witness, Vec, // F_X coeffs Vec, // K_X coeffs @@ -97,7 +95,7 @@ where let delta = transcript.get_challenge(); let deltas = exponential_powers(delta, t); - let mut f_z = r1cs.eval_relation(&z)?; + let mut f_z = r1cs.eval_at_z(&z)?; if f_z.len() != m { return Err(Error::NotSameLength( "number of constraints in R1CS".to_string(), @@ -127,15 +125,18 @@ where // sanity check: check that the new randomized instance (the original instance but with // 'refreshed' randomness) satisfies the relation. #[cfg(test)] - r1cs.check_relaxed_relation( - w, - &CommittedInstance { - phi: instance.phi, - betas: betas_star.clone(), - e: F_alpha, - x: instance.x.clone(), - }, - )?; + { + use crate::arith::Arith; + r1cs.check_relation( + w, + &CommittedInstance::<_, true> { + phi: instance.phi, + betas: betas_star.clone(), + e: F_alpha, + x: instance.x.clone(), + }, + )?; + } let zs: Vec> = std::iter::once(z.clone()) .chain( @@ -178,7 +179,7 @@ where inner[j] += Lh * zj; } } - let f_ev = r1cs.eval_relation(&inner)?; + let f_ev = r1cs.eval_at_z(&inner)?; G_evals[hi] = cfg_into_iter!(f_ev) .enumerate() @@ -253,13 +254,13 @@ where pub fn verify( transcript: &mut impl Transcript, // running instance - instance: &CommittedInstance, + instance: &CommittedInstance, // incoming instances - vec_instances: &[CommittedInstance], + vec_instances: &[CommittedInstance], // polys from P F_coeffs: Vec, K_coeffs: Vec, - ) -> Result, Error> { + ) -> Result, Error> { let t = instance.betas.len(); // absorb the committed instances @@ -395,6 +396,7 @@ pub mod tests { use ark_std::{rand::Rng, UniformRand}; use crate::arith::r1cs::tests::{get_test_r1cs, get_test_z_split}; + use crate::arith::Arith; use crate::commitment::{pedersen::Pedersen, CommitmentScheme}; use crate::transcript::poseidon::poseidon_canonical_config; @@ -419,9 +421,9 @@ pub mod tests { k: usize, ) -> ( Witness, - CommittedInstance, + CommittedInstance, Vec>, - Vec>, + Vec>, ) { let mut rng = ark_std::test_rng(); @@ -439,7 +441,7 @@ pub mod tests { r_w: C::ScalarField::zero(), }; let phi = Pedersen::::commit(&pedersen_params, &witness.w, &witness.r_w).unwrap(); - let instance = CommittedInstance:: { + let instance = CommittedInstance:: { phi, betas: betas.clone(), e: C::ScalarField::zero(), @@ -447,7 +449,7 @@ pub mod tests { }; // same for the other instances let mut witnesses: Vec> = Vec::new(); - let mut instances: Vec> = Vec::new(); + let mut instances: Vec> = Vec::new(); #[allow(clippy::needless_range_loop)] for _ in 0..k { let (_, x_i, w_i) = get_test_z_split::(rng.gen::() as usize); @@ -457,7 +459,7 @@ pub mod tests { }; let phi_i = Pedersen::::commit(&pedersen_params, &witness_i.w, &witness_i.r_w).unwrap(); - let instance_i = CommittedInstance:: { + let instance_i = CommittedInstance:: { phi: phi_i, betas: vec![], e: C::ScalarField::zero(), @@ -509,7 +511,7 @@ pub mod tests { assert!(!folded_instance.e.is_zero()); // check that the folded instance satisfies the relation - r1cs.check_relaxed_relation(&folded_witness, &folded_instance) + r1cs.check_relation(&folded_witness, &folded_instance) .unwrap(); } @@ -558,7 +560,7 @@ pub mod tests { assert!(!folded_instance.e.is_zero()); // check that the folded instance satisfies the relation - r1cs.check_relaxed_relation(&folded_witness, &folded_instance) + r1cs.check_relation(&folded_witness, &folded_instance) .unwrap(); running_witness = folded_witness; diff --git a/folding-schemes/src/folding/protogalaxy/mod.rs b/folding-schemes/src/folding/protogalaxy/mod.rs index 37b2836..be9eb1f 100644 --- a/folding-schemes/src/folding/protogalaxy/mod.rs +++ b/folding-schemes/src/folding/protogalaxy/mod.rs @@ -18,10 +18,14 @@ use ark_relations::r1cs::{ use ark_std::{ borrow::Borrow, cmp::max, fmt::Debug, log2, marker::PhantomData, rand::RngCore, One, Zero, }; +use constants::{INCOMING, RUNNING}; use num_bigint::BigUint; use crate::{ - arith::r1cs::{extract_r1cs, extract_w_x, RelaxedR1CS, R1CS}, + arith::{ + r1cs::{extract_r1cs, extract_w_x, R1CS}, + Arith, + }, commitment::CommitmentScheme, folding::circuits::{ cyclefold::{ @@ -37,6 +41,7 @@ use crate::{ }; pub mod circuits; +pub mod constants; pub mod folding; pub mod traits; pub(crate) mod utils; @@ -44,7 +49,9 @@ pub(crate) mod utils; use circuits::AugmentedFCircuit; use folding::Folding; -use super::traits::{CommittedInstanceOps, CommittedInstanceVarOps, WitnessOps, WitnessVarOps}; +use super::traits::{ + CommittedInstanceOps, CommittedInstanceVarOps, Dummy, WitnessOps, WitnessVarOps, +}; /// Configuration for ProtoGalaxy's CycleFold circuit pub struct ProtoGalaxyCycleFoldConfig { @@ -62,51 +69,68 @@ impl CycleFoldConfig for ProtoGalaxyCycleFoldConfig { /// in ProtoGalaxy instances. pub type ProtoGalaxyCycleFoldCircuit = CycleFoldCircuit, GC>; +/// The committed instance of ProtoGalaxy. +/// +/// We use `TYPE` to distinguish between incoming and running instances, as +/// they have slightly different structures (e.g., length of `betas`) and +/// behaviors (e.g., in satisfiability checks). #[derive(Clone, Debug, PartialEq, Eq)] -pub struct CommittedInstance { +pub struct CommittedInstance { phi: C, betas: Vec, e: C::ScalarField, x: Vec, } -impl CommittedInstance { - pub fn dummy_running(io_len: usize, t: usize) -> Self { +impl Dummy<(usize, usize)> for CommittedInstance { + fn dummy((io_len, t): (usize, usize)) -> Self { + if TYPE == INCOMING { + assert_eq!(t, 0); + } Self { phi: C::zero(), - betas: vec![C::ScalarField::zero(); t], - e: C::ScalarField::zero(), - x: vec![C::ScalarField::zero(); io_len], + betas: vec![Zero::zero(); t], + e: Zero::zero(), + x: vec![Zero::zero(); io_len], } } +} - pub fn dummy_incoming(io_len: usize) -> Self { - Self::dummy_running(io_len, 0) +impl Dummy<&R1CS>> for CommittedInstance { + fn dummy(r1cs: &R1CS>) -> Self { + let t = if TYPE == RUNNING { + log2(r1cs.num_constraints()) as usize + } else { + 0 + }; + Self::dummy((r1cs.num_public_inputs(), t)) } } -impl CommittedInstanceOps for CommittedInstance { - type Var = CommittedInstanceVar; +impl CommittedInstanceOps for CommittedInstance { + type Var = CommittedInstanceVar; fn get_commitments(&self) -> Vec { vec![self.phi] } fn is_incoming(&self) -> bool { - self.e == Zero::zero() && self.betas.is_empty() + TYPE == INCOMING } } #[derive(Clone, Debug)] -pub struct CommittedInstanceVar { +pub struct CommittedInstanceVar { phi: NonNativeAffineVar, betas: Vec>, e: FpVar, x: Vec>, } -impl AllocVar, C::ScalarField> for CommittedInstanceVar { - fn new_variable>>( +impl AllocVar, C::ScalarField> + for CommittedInstanceVar +{ + fn new_variable>>( cs: impl Into>, f: impl FnOnce() -> Result, mode: AllocationMode, @@ -119,15 +143,19 @@ impl AllocVar, C::ScalarField> for Committed Ok(Self { phi: NonNativeAffineVar::new_variable(cs.clone(), || Ok(u.phi), mode)?, betas: Vec::new_variable(cs.clone(), || Ok(u.betas.clone()), mode)?, - e: FpVar::new_variable(cs.clone(), || Ok(u.e), mode)?, + e: if TYPE == RUNNING { + FpVar::new_variable(cs.clone(), || Ok(u.e), mode)? + } else { + FpVar::zero() + }, x: Vec::new_variable(cs.clone(), || Ok(u.x.clone()), mode)?, }) }) } } -impl R1CSVar for CommittedInstanceVar { - type Value = CommittedInstance; +impl R1CSVar for CommittedInstanceVar { + type Value = CommittedInstance; fn cs(&self) -> ConstraintSystemRef { self.phi @@ -151,7 +179,7 @@ impl R1CSVar for CommittedInstanceVar { } } -impl CommittedInstanceVarOps for CommittedInstanceVar { +impl CommittedInstanceVarOps for CommittedInstanceVar { type PointVar = NonNativeAffineVar; fn get_commitments(&self) -> Vec { @@ -163,11 +191,13 @@ impl CommittedInstanceVarOps for CommittedInstanceVar { } fn enforce_incoming(&self) -> Result<(), SynthesisError> { - if self.betas.is_empty() { - self.e.enforce_equal(&FpVar::zero()) - } else { - Err(SynthesisError::Unsatisfiable) - } + // We don't need to check if `self` is an incoming instance in-circuit, + // because incoming instances and running instances already have + // different types of `e` (constant vs witness) when we allocate them + // in-circuit. + (TYPE == INCOMING) + .then_some(()) + .ok_or(SynthesisError::Unsatisfiable) } fn enforce_partial_equal(&self, other: &Self) -> Result<(), SynthesisError> { @@ -195,9 +225,9 @@ impl Witness { &self, params: &CS::ProverParams, x: Vec, - ) -> Result, crate::Error> { + ) -> Result, crate::Error> { let phi = CS::commit(params, &self.w, &self.r_w)?; - Ok(CommittedInstance { + Ok(CommittedInstance:: { phi, x, e: F::zero(), @@ -206,6 +236,15 @@ impl Witness { } } +impl Dummy<&R1CS> for Witness { + fn dummy(r1cs: &R1CS) -> Self { + Self { + w: vec![F::zero(); r1cs.num_witnesses()], + r_w: F::zero(), + } + } +} + impl WitnessOps for Witness { type Var = WitnessVar; @@ -357,9 +396,9 @@ where pub z_i: Vec, /// ProtoGalaxy instances pub w_i: Witness, - pub u_i: CommittedInstance, + pub u_i: CommittedInstance, pub W_i: Witness, - pub U_i: CommittedInstance, + pub U_i: CommittedInstance, /// CycleFold running instance pub cf_W_i: CycleFoldWitness, @@ -492,9 +531,10 @@ where type PreprocessorParam = (PoseidonConfig>, FC); type ProverParam = ProverParams; type VerifierParam = VerifierParams; - type RunningInstance = (CommittedInstance, Witness); - type IncomingInstance = (CommittedInstance, Witness); - type MultiCommittedInstanceWithWitness = (CommittedInstance, Witness); + type RunningInstance = (CommittedInstance, Witness); + type IncomingInstance = (CommittedInstance, Witness); + type MultiCommittedInstanceWithWitness = + (CommittedInstance, Witness); type CFInstance = (CycleFoldCommittedInstance, CycleFoldWitness); fn preprocess( @@ -560,9 +600,9 @@ where let pp_hash = vp.pp_hash()?; // setup the dummy instances - let (W_dummy, U_dummy) = vp.r1cs.dummy_running_instance(); - let (w_dummy, u_dummy) = vp.r1cs.dummy_incoming_instance(); - let (cf_W_dummy, cf_U_dummy) = vp.cf_r1cs.dummy_running_instance(); + let (w_dummy, u_dummy) = vp.r1cs.dummy_witness_instance(); + let (W_dummy, U_dummy) = vp.r1cs.dummy_witness_instance(); + let (cf_W_dummy, cf_U_dummy) = vp.cf_r1cs.dummy_witness_instance(); // W_dummy=W_0 is a 'dummy witness', all zeroes, but with the size corresponding to the // R1CS that we're working with. @@ -808,10 +848,11 @@ where )?, U_i1 ); - self.cf_r1cs.check_tight_relation(&_cf1_w_i, &cf1_u_i)?; - self.cf_r1cs.check_tight_relation(&_cf2_w_i, &cf2_u_i)?; - self.cf_r1cs - .check_relaxed_relation(&self.cf_W_i, &self.cf_U_i)?; + cf1_u_i.check_incoming()?; + cf2_u_i.check_incoming()?; + self.cf_r1cs.check_relation(&_cf1_w_i, &cf1_u_i)?; + self.cf_r1cs.check_relation(&_cf2_w_i, &cf2_u_i)?; + self.cf_r1cs.check_relation(&self.cf_W_i, &self.cf_U_i)?; } self.W_i = W_i1; @@ -846,8 +887,9 @@ where #[cfg(test)] { - self.r1cs.check_tight_relation(&self.w_i, &self.u_i)?; - self.r1cs.check_relaxed_relation(&self.W_i, &self.U_i)?; + self.u_i.check_incoming()?; + self.r1cs.check_relation(&self.w_i, &self.u_i)?; + self.r1cs.check_relation(&self.W_i, &self.U_i)?; } Ok(()) @@ -904,13 +946,15 @@ where return Err(Error::IVCVerificationFail); } - // check R1CS satisfiability - vp.r1cs.check_tight_relation(&w_i, &u_i)?; + // check R1CS satisfiability, which is equivalent to checking if `u_i` + // is an incoming instance and if `w_i` and `u_i` satisfy RelaxedR1CS + u_i.check_incoming()?; + vp.r1cs.check_relation(&w_i, &u_i)?; // check RelaxedR1CS satisfiability - vp.r1cs.check_relaxed_relation(&W_i, &U_i)?; + vp.r1cs.check_relation(&W_i, &U_i)?; // check CycleFold RelaxedR1CS satisfiability - vp.cf_r1cs.check_relaxed_relation(&cf_W_i, &cf_U_i)?; + vp.cf_r1cs.check_relation(&cf_W_i, &cf_U_i)?; Ok(()) } diff --git a/folding-schemes/src/folding/protogalaxy/traits.rs b/folding-schemes/src/folding/protogalaxy/traits.rs index d67f79e..51b2aa1 100644 --- a/folding-schemes/src/folding/protogalaxy/traits.rs +++ b/folding-schemes/src/folding/protogalaxy/traits.rs @@ -3,18 +3,20 @@ use ark_ec::CurveGroup; use ark_ff::PrimeField; use ark_r1cs_std::{fields::fp::FpVar, uint8::UInt8, ToConstraintFieldGadget}; use ark_relations::r1cs::SynthesisError; -use ark_std::{cfg_iter, log2, rand::RngCore, One, Zero}; +use ark_std::{cfg_into_iter, log2, One}; use rayon::prelude::*; -use super::{utils::pow_i, CommittedInstance, CommittedInstanceVar, Witness}; +use super::{constants::RUNNING, utils::pow_i, CommittedInstance, CommittedInstanceVar, Witness}; use crate::{ - arith::r1cs::{RelaxedR1CS, R1CS}, + arith::{r1cs::R1CS, Arith}, + folding::circuits::CF1, transcript::AbsorbNonNative, + utils::vec::is_zero_vec, Error, }; // Implements the trait for absorbing ProtoGalaxy's CommittedInstance. -impl Absorb for CommittedInstance +impl Absorb for CommittedInstance where C::ScalarField: Absorb, { @@ -33,7 +35,9 @@ where } // Implements the trait for absorbing ProtoGalaxy's CommittedInstanceVar in-circuit. -impl AbsorbGadget for CommittedInstanceVar { +impl AbsorbGadget + for CommittedInstanceVar +{ fn to_sponge_bytes(&self) -> Result>, SynthesisError> { FpVar::batch_to_sponge_bytes(&self.to_sponge_field_elements()?) } @@ -49,69 +53,49 @@ impl AbsorbGadget for CommittedInstanceVar { } } -impl RelaxedR1CS, CommittedInstance> - for R1CS +/// Implements `Arith` for R1CS, where the witness is of type [`Witness`], and +/// the committed instance is of type [`CommittedInstance`]. +/// +/// Due to the error term `CommittedInstance.e`, R1CS here is considered as a +/// relaxed R1CS. +/// +/// See `nova/traits.rs` for the rationale behind the design. +impl Arith>, CommittedInstance> + for R1CS> { - fn dummy_running_instance(&self) -> (Witness, CommittedInstance) { - let w_len = self.A.n_cols - 1 - self.l; - let w_dummy = Witness::new(vec![C::ScalarField::zero(); w_len]); - let u_dummy = CommittedInstance::::dummy_running(self.l, log2(self.A.n_rows) as usize); - (w_dummy, u_dummy) - } - - fn dummy_incoming_instance(&self) -> (Witness, CommittedInstance) { - let w_len = self.A.n_cols - 1 - self.l; - let w_dummy = Witness::new(vec![C::ScalarField::zero(); w_len]); - let u_dummy = CommittedInstance::::dummy_incoming(self.l); - (w_dummy, u_dummy) - } - - fn is_relaxed(_w: &Witness, u: &CommittedInstance) -> bool { - u.e != C::ScalarField::zero() || !u.betas.is_empty() - } + type Evaluation = Vec>; - fn extract_z(w: &Witness, u: &CommittedInstance) -> Vec { - [&[C::ScalarField::one()][..], &u.x, &w.w].concat() + fn eval_relation( + &self, + w: &Witness>, + u: &CommittedInstance, + ) -> Result { + self.eval_at_z(&[&[C::ScalarField::one()][..], &u.x, &w.w].concat()) } - fn check_error_terms( + fn check_evaluation( _w: &Witness, - u: &CommittedInstance, + u: &CommittedInstance, e: Vec, ) -> Result<(), Error> { - if u.betas.len() != log2(e.len()) as usize { - return Err(Error::NotSameLength( - "instance.betas.len()".to_string(), - u.betas.len(), - "log2(e.len())".to_string(), - log2(e.len()) as usize, - )); - } - - let r = cfg_iter!(e) - .enumerate() - .map(|(i, e_i)| pow_i(i, &u.betas) * e_i) - .sum(); - if u.e == r { - Ok(()) + let ok = if TYPE == RUNNING { + if u.betas.len() != log2(e.len()) as usize { + return Err(Error::NotSameLength( + "instance.betas.len()".to_string(), + u.betas.len(), + "log2(e.len())".to_string(), + log2(e.len()) as usize, + )); + } + + u.e == cfg_into_iter!(e) + .enumerate() + .map(|(i, e_i)| pow_i(i, &u.betas) * e_i) + .sum::>() } else { - Err(Error::NotSatisfied) - } - } - - fn sample( - &self, - _params: &CS::ProverParams, - _rng: impl RngCore, - ) -> Result<(Witness, CommittedInstance), Error> - where - CS: crate::commitment::CommitmentScheme, - { - // Sampling a random pair of witness and committed instance is required - // for the zero-knowledge layer for ProtoGalaxy, which is not supported - // yet. - // Tracking issue: https://github.com/privacy-scaling-explorations/sonobe/issues/82 - unimplemented!() + is_zero_vec(&e) + }; + ok.then_some(()).ok_or(Error::NotSatisfied) } } @@ -133,7 +117,7 @@ pub mod tests { let t = rng.gen::() as usize; let io_len = rng.gen::() as usize; - let ci = CommittedInstance:: { + let ci = CommittedInstance:: { phi: Projective::rand(&mut rng), betas: (0..t).map(|_| Fr::rand(&mut rng)).collect(), e: Fr::rand(&mut rng), @@ -146,7 +130,8 @@ pub mod tests { let cs = ConstraintSystem::::new_ref(); let ciVar = - CommittedInstanceVar::::new_witness(cs.clone(), || Ok(ci.clone())).unwrap(); + CommittedInstanceVar::::new_witness(cs.clone(), || Ok(ci.clone())) + .unwrap(); let bytes_var = ciVar.to_sponge_bytes().unwrap(); let field_elements_var = ciVar.to_sponge_field_elements().unwrap(); diff --git a/folding-schemes/src/folding/traits.rs b/folding-schemes/src/folding/traits.rs index 5815397..3890554 100644 --- a/folding-schemes/src/folding/traits.rs +++ b/folding-schemes/src/folding/traits.rs @@ -119,3 +119,13 @@ pub trait WitnessVarOps { /// randomness) contained in the witness. fn get_openings(&self) -> Vec<(&[FpVar], FpVar)>; } + +pub trait Dummy { + fn dummy(cfg: Cfg) -> Self; +} + +impl Dummy for Vec { + fn dummy(cfg: usize) -> Self { + vec![Default::default(); cfg] + } +} diff --git a/folding-schemes/src/utils/mod.rs b/folding-schemes/src/utils/mod.rs index b1bad97..c63938f 100644 --- a/folding-schemes/src/utils/mod.rs +++ b/folding-schemes/src/utils/mod.rs @@ -8,7 +8,7 @@ use ark_serialize::CanonicalSerialize; use ark_std::Zero; use sha3::{Digest, Sha3_256}; -use crate::arith::Arith; +use crate::arith::ArithSerializer; use crate::commitment::CommitmentScheme; use crate::Error; @@ -45,8 +45,8 @@ pub fn get_cm_coordinates(cm: &C) -> Vec { /// returns the hash of the given public parameters of the Folding Scheme pub fn pp_hash( - arith: &impl Arith, - cf_arith: &impl Arith, + arith: &impl ArithSerializer, + cf_arith: &impl ArithSerializer, cs_vp: &CS1::VerifierParams, cf_cs_vp: &CS2::VerifierParams, poseidon_config: &PoseidonConfig,