arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/folding-schemes/src/folding/hypernova/lcccs.rs
folding-schemes/src/folding/hypernova/lcccs.rsRaw
  1use ark_ec::CurveGroup;
  2use ark_ff::PrimeField;
  3use ark_poly::DenseMultilinearExtension;
  4use ark_poly::MultilinearExtension;
  5
  6use ark_std::rand::Rng;
  7
  8use super::cccs::Witness;
  9use crate::ccs::CCS;
 10use crate::commitment::{
 11    pedersen::{Params as PedersenParams, Pedersen},
 12    CommitmentScheme,
 13};
 14use crate::utils::mle::dense_vec_to_dense_mle;
 15use crate::utils::vec::mat_vec_mul;
 16use crate::Error;
 17
 18/// Linearized Committed CCS instance
 19#[derive(Debug, Clone, Eq, PartialEq)]
 20pub struct LCCCS<C: CurveGroup> {
 21    // Commitment to witness
 22    pub C: C,
 23    // Relaxation factor of z for folded LCCCS
 24    pub u: C::ScalarField,
 25    // Public input/output
 26    pub x: Vec<C::ScalarField>,
 27    // Random evaluation point for the v_i
 28    pub r_x: Vec<C::ScalarField>,
 29    // Vector of v_i
 30    pub v: Vec<C::ScalarField>,
 31}
 32
 33impl<F: PrimeField> CCS<F> {
 34    pub fn to_lcccs<R: Rng, C: CurveGroup>(
 35        &self,
 36        rng: &mut R,
 37        pedersen_params: &PedersenParams<C>,
 38        z: &[C::ScalarField],
 39    ) -> Result<(LCCCS<C>, Witness<C::ScalarField>), Error>
 40    where
 41        // enforce that CCS's F is the C::ScalarField
 42        C: CurveGroup<ScalarField = F>,
 43    {
 44        let w: Vec<C::ScalarField> = z[(1 + self.l)..].to_vec();
 45        let r_w = C::ScalarField::rand(rng);
 46        let C = Pedersen::<C, true>::commit(pedersen_params, &w, &r_w)?;
 47
 48        let r_x: Vec<C::ScalarField> = (0..self.s).map(|_| C::ScalarField::rand(rng)).collect();
 49
 50        let Mzs: Vec<DenseMultilinearExtension<F>> = self
 51            .M
 52            .iter()
 53            .map(|M_j| Ok(dense_vec_to_dense_mle(self.s, &mat_vec_mul(M_j, z)?)))
 54            .collect::<Result<_, Error>>()?;
 55
 56        // compute v_j
 57        let v: Vec<F> = Mzs
 58            .iter()
 59            .map(|Mz| Mz.evaluate(&r_x).ok_or(Error::EvaluationFail))
 60            .collect::<Result<_, Error>>()?;
 61
 62        Ok((
 63            LCCCS::<C> {
 64                C,
 65                u: C::ScalarField::one(),
 66                x: z[1..(1 + self.l)].to_vec(),
 67                r_x,
 68                v,
 69            },
 70            Witness::<C::ScalarField> { w, r_w },
 71        ))
 72    }
 73}
 74
 75impl<C: CurveGroup> LCCCS<C> {
 76    /// Perform the check of the LCCCS instance described at section 4.2
 77    pub fn check_relation(
 78        &self,
 79        pedersen_params: &PedersenParams<C>,
 80        ccs: &CCS<C::ScalarField>,
 81        w: &Witness<C::ScalarField>,
 82    ) -> Result<(), Error> {
 83        // check that C is the commitment of w. Notice that this is not verifying a Pedersen
 84        // opening, but checking that the Commitment comes from committing to the witness.
 85        if self.C != Pedersen::<C, true>::commit(pedersen_params, &w.w, &w.r_w)? {
 86            return Err(Error::NotSatisfied);
 87        }
 88
 89        // check CCS relation
 90        let z: Vec<C::ScalarField> = [vec![self.u], self.x.clone(), w.w.to_vec()].concat();
 91
 92        let computed_v: Vec<C::ScalarField> = ccs
 93            .M
 94            .iter()
 95            .map(|M_j| {
 96                let Mz_mle = dense_vec_to_dense_mle(ccs.s, &mat_vec_mul(M_j, &z)?);
 97                Mz_mle.evaluate(&self.r_x).ok_or(Error::EvaluationFail)
 98            })
 99            .collect::<Result<_, Error>>()?;
100        if computed_v != self.v {
101            return Err(Error::NotSatisfied);
102        }
103        Ok(())
104    }
105}
106
107#[cfg(test)]
108pub mod tests {
109    use ark_pallas::{Fr, Projective};
110    use ark_std::test_rng;
111    use ark_std::One;
112    use ark_std::UniformRand;
113    use ark_std::Zero;
114    use std::sync::Arc;
115
116    use super::*;
117    use crate::ccs::{
118        r1cs::R1CS,
119        tests::{get_test_ccs, get_test_z},
120    };
121    use crate::utils::hypercube::BooleanHypercube;
122    use crate::utils::virtual_polynomial::{build_eq_x_r_vec, VirtualPolynomial};
123
124    // method for testing
125    pub fn compute_Ls<C: CurveGroup>(
126        ccs: &CCS<C::ScalarField>,
127        lcccs: &LCCCS<C>,
128        z: &[C::ScalarField],
129    ) -> Vec<VirtualPolynomial<C::ScalarField>> {
130        let eq_rx = build_eq_x_r_vec(&lcccs.r_x).unwrap();
131        let eq_rx_mle = dense_vec_to_dense_mle(ccs.s, &eq_rx);
132
133        let mut Ls = Vec::with_capacity(ccs.t);
134        for M_j in ccs.M.iter() {
135            let mut L = VirtualPolynomial::<C::ScalarField>::new(ccs.s);
136            let mut Mz = vec![dense_vec_to_dense_mle(ccs.s, &mat_vec_mul(M_j, z).unwrap())];
137            Mz.push(eq_rx_mle.clone());
138            L.add_mle_list(
139                Mz.iter().map(|v| Arc::new(v.clone())),
140                C::ScalarField::one(),
141            )
142            .unwrap();
143            Ls.push(L);
144        }
145        Ls
146    }
147
148    #[test]
149    /// Test linearized CCCS v_j against the L_j(x)
150    fn test_lcccs_v_j() {
151        let mut rng = test_rng();
152
153        let n_rows = 2_u32.pow(5) as usize;
154        let n_cols = 2_u32.pow(5) as usize;
155        let r1cs = R1CS::rand(&mut rng, n_rows, n_cols);
156        let ccs = CCS::from_r1cs(r1cs);
157        let z: Vec<Fr> = (0..n_cols).map(|_| Fr::rand(&mut rng)).collect();
158
159        let (pedersen_params, _) =
160            Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap();
161
162        let (lcccs, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z).unwrap();
163        // with our test vector coming from R1CS, v should have length 3
164        assert_eq!(lcccs.v.len(), 3);
165
166        let vec_L_j_x = compute_Ls(&ccs, &lcccs, &z);
167        assert_eq!(vec_L_j_x.len(), lcccs.v.len());
168
169        for (v_i, L_j_x) in lcccs.v.into_iter().zip(vec_L_j_x) {
170            let sum_L_j_x = BooleanHypercube::new(ccs.s)
171                .map(|y| L_j_x.evaluate(&y).unwrap())
172                .fold(Fr::zero(), |acc, result| acc + result);
173            assert_eq!(v_i, sum_L_j_x);
174        }
175    }
176
177    /// Given a bad z, check that the v_j should not match with the L_j(x)
178    #[test]
179    fn test_bad_v_j() {
180        let mut rng = test_rng();
181
182        let ccs = get_test_ccs();
183        let z = get_test_z(3);
184        ccs.check_relation(&z.clone()).unwrap();
185
186        // Mutate z so that the relation does not hold
187        let mut bad_z = z.clone();
188        bad_z[3] = Fr::zero();
189        assert!(ccs.check_relation(&bad_z.clone()).is_err());
190
191        let (pedersen_params, _) =
192            Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap();
193        // Compute v_j with the right z
194        let (lcccs, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z).unwrap();
195        // with our test vector coming from R1CS, v should have length 3
196        assert_eq!(lcccs.v.len(), 3);
197
198        // Bad compute L_j(x) with the bad z
199        let vec_L_j_x = compute_Ls(&ccs, &lcccs, &bad_z);
200        assert_eq!(vec_L_j_x.len(), lcccs.v.len());
201
202        // Make sure that the LCCCS is not satisfied given these L_j(x)
203        // i.e. summing L_j(x) over the hypercube should not give v_j for all j
204        let mut satisfied = true;
205        for (v_i, L_j_x) in lcccs.v.into_iter().zip(vec_L_j_x) {
206            let sum_L_j_x = BooleanHypercube::new(ccs.s)
207                .map(|y| L_j_x.evaluate(&y).unwrap())
208                .fold(Fr::zero(), |acc, result| acc + result);
209            if v_i != sum_L_j_x {
210                satisfied = false;
211            }
212        }
213        assert!(!satisfied);
214    }
215}