arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/folding-schemes/src/folding/hypernova/lcccs.rs
folding-schemes/src/folding/hypernova/lcccs.rsRaw
  1use ark_crypto_primitives::sponge::Absorb;
  2use ark_ec::CurveGroup;
  3use ark_ff::PrimeField;
  4use ark_poly::DenseMultilinearExtension;
  5use ark_poly::MultilinearExtension;
  6use ark_serialize::CanonicalDeserialize;
  7use ark_serialize::CanonicalSerialize;
  8use ark_std::rand::Rng;
  9use ark_std::Zero;
 10
 11use super::circuits::LCCCSVar;
 12use super::Witness;
 13use crate::arith::ccs::CCS;
 14use crate::arith::Arith;
 15use crate::commitment::CommitmentScheme;
 16use crate::folding::circuits::CF1;
 17use crate::folding::traits::{CommittedInstanceOps, Dummy};
 18use crate::transcript::AbsorbNonNative;
 19use crate::utils::mle::dense_vec_to_dense_mle;
 20use crate::utils::vec::mat_vec_mul;
 21use crate::Error;
 22
 23/// Linearized Committed CCS instance
 24#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
 25pub struct LCCCS<C: CurveGroup> {
 26    // Commitment to witness
 27    pub C: C,
 28    // Relaxation factor of z for folded LCCCS
 29    pub u: C::ScalarField,
 30    // Public input/output
 31    pub x: Vec<C::ScalarField>,
 32    // Random evaluation point for the v_i
 33    pub r_x: Vec<C::ScalarField>,
 34    // Vector of v_i
 35    pub v: Vec<C::ScalarField>,
 36}
 37
 38impl<F: PrimeField> CCS<F> {
 39    pub fn to_lcccs<R: Rng, C, CS: CommitmentScheme<C, H>, const H: bool>(
 40        &self,
 41        rng: &mut R,
 42        cs_params: &CS::ProverParams,
 43        z: &[C::ScalarField],
 44    ) -> Result<(LCCCS<C>, Witness<C::ScalarField>), Error>
 45    where
 46        // enforce that CCS's F is the C::ScalarField
 47        C: CurveGroup<ScalarField = F>,
 48    {
 49        let w: Vec<C::ScalarField> = z[(1 + self.l)..].to_vec();
 50        // if the commitment scheme is set to be hiding, set the random blinding parameter
 51        let r_w = if CS::is_hiding() {
 52            C::ScalarField::rand(rng)
 53        } else {
 54            C::ScalarField::zero()
 55        };
 56        let C = CS::commit(cs_params, &w, &r_w)?;
 57
 58        let r_x: Vec<C::ScalarField> = (0..self.s).map(|_| C::ScalarField::rand(rng)).collect();
 59
 60        let Mzs: Vec<DenseMultilinearExtension<F>> = self
 61            .M
 62            .iter()
 63            .map(|M_j| Ok(dense_vec_to_dense_mle(self.s, &mat_vec_mul(M_j, z)?)))
 64            .collect::<Result<_, Error>>()?;
 65
 66        // compute v_j
 67        let v: Vec<F> = Mzs
 68            .iter()
 69            .map(|Mz| Mz.evaluate(&r_x).ok_or(Error::EvaluationFail))
 70            .collect::<Result<_, Error>>()?;
 71
 72        Ok((
 73            LCCCS::<C> {
 74                C,
 75                u: C::ScalarField::one(),
 76                x: z[1..(1 + self.l)].to_vec(),
 77                r_x,
 78                v,
 79            },
 80            Witness::<C::ScalarField> { w, r_w },
 81        ))
 82    }
 83}
 84
 85impl<C: CurveGroup> Dummy<&CCS<CF1<C>>> for LCCCS<C> {
 86    fn dummy(ccs: &CCS<CF1<C>>) -> Self {
 87        Self {
 88            C: C::zero(),
 89            u: CF1::<C>::zero(),
 90            x: vec![CF1::<C>::zero(); ccs.l],
 91            r_x: vec![CF1::<C>::zero(); ccs.s],
 92            v: vec![CF1::<C>::zero(); ccs.t],
 93        }
 94    }
 95}
 96
 97impl<C: CurveGroup> Arith<Witness<CF1<C>>, LCCCS<C>> for CCS<CF1<C>> {
 98    type Evaluation = Vec<CF1<C>>;
 99
100    /// Perform the check of the LCCCS instance described at section 4.2,
101    /// notice that this method does not check the commitment correctness
102    fn eval_relation(&self, w: &Witness<CF1<C>>, u: &LCCCS<C>) -> Result<Self::Evaluation, Error> {
103        let z = [&[u.u][..], &u.x, &w.w].concat();
104
105        self.M
106            .iter()
107            .map(|M_j| {
108                let Mz_mle = dense_vec_to_dense_mle(self.s, &mat_vec_mul(M_j, &z)?);
109                Mz_mle.evaluate(&u.r_x).ok_or(Error::EvaluationFail)
110            })
111            .collect()
112    }
113
114    fn check_evaluation(
115        _w: &Witness<CF1<C>>,
116        u: &LCCCS<C>,
117        e: Self::Evaluation,
118    ) -> Result<(), Error> {
119        (u.v == e).then_some(()).ok_or(Error::NotSatisfied)
120    }
121}
122
123impl<C: CurveGroup> Absorb for LCCCS<C>
124where
125    C::ScalarField: Absorb,
126{
127    fn to_sponge_bytes(&self, dest: &mut Vec<u8>) {
128        C::ScalarField::batch_to_sponge_bytes(&self.to_sponge_field_elements_as_vec(), dest);
129    }
130
131    fn to_sponge_field_elements<F: PrimeField>(&self, dest: &mut Vec<F>) {
132        // We cannot call `to_native_sponge_field_elements(dest)` directly, as
133        // `to_native_sponge_field_elements` needs `F` to be `C::ScalarField`,
134        // but here `F` is a generic `PrimeField`.
135        self.C
136            .to_native_sponge_field_elements_as_vec()
137            .to_sponge_field_elements(dest);
138        self.u.to_sponge_field_elements(dest);
139        self.x.to_sponge_field_elements(dest);
140        self.r_x.to_sponge_field_elements(dest);
141        self.v.to_sponge_field_elements(dest);
142    }
143}
144
145impl<C: CurveGroup> CommittedInstanceOps<C> for LCCCS<C> {
146    type Var = LCCCSVar<C>;
147
148    fn get_commitments(&self) -> Vec<C> {
149        vec![self.C]
150    }
151
152    fn is_incoming(&self) -> bool {
153        false
154    }
155}
156
157#[cfg(test)]
158pub mod tests {
159    use ark_pallas::{Fr, Projective};
160    use ark_std::test_rng;
161    use ark_std::One;
162    use ark_std::UniformRand;
163    use std::sync::Arc;
164
165    use super::*;
166    use crate::arith::{
167        ccs::tests::{get_test_ccs, get_test_z},
168        r1cs::R1CS,
169        Arith,
170    };
171    use crate::commitment::pedersen::Pedersen;
172    use crate::utils::hypercube::BooleanHypercube;
173    use crate::utils::virtual_polynomial::{build_eq_x_r_vec, VirtualPolynomial};
174
175    // method for testing
176    pub fn compute_Ls<C: CurveGroup>(
177        ccs: &CCS<C::ScalarField>,
178        lcccs: &LCCCS<C>,
179        z: &[C::ScalarField],
180    ) -> Vec<VirtualPolynomial<C::ScalarField>> {
181        let eq_rx = build_eq_x_r_vec(&lcccs.r_x).unwrap();
182        let eq_rx_mle = dense_vec_to_dense_mle(ccs.s, &eq_rx);
183
184        let mut Ls = Vec::with_capacity(ccs.t);
185        for M_j in ccs.M.iter() {
186            let mut L = VirtualPolynomial::<C::ScalarField>::new(ccs.s);
187            let mut Mz = vec![dense_vec_to_dense_mle(ccs.s, &mat_vec_mul(M_j, z).unwrap())];
188            Mz.push(eq_rx_mle.clone());
189            L.add_mle_list(
190                Mz.iter().map(|v| Arc::new(v.clone())),
191                C::ScalarField::one(),
192            )
193            .unwrap();
194            Ls.push(L);
195        }
196        Ls
197    }
198
199    #[test]
200    /// Test linearized CCCS v_j against the L_j(x)
201    fn test_lcccs_v_j() {
202        let mut rng = test_rng();
203
204        let n_rows = 2_u32.pow(5) as usize;
205        let n_cols = 2_u32.pow(5) as usize;
206        let r1cs = R1CS::<Fr>::rand(&mut rng, n_rows, n_cols);
207        let ccs = CCS::from(r1cs);
208        let z: Vec<Fr> = (0..n_cols).map(|_| Fr::rand(&mut rng)).collect();
209
210        let (pedersen_params, _) =
211            Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap();
212
213        let (lcccs, _) = ccs
214            .to_lcccs::<_, Projective, Pedersen<Projective, false>, false>(
215                &mut rng,
216                &pedersen_params,
217                &z,
218            )
219            .unwrap();
220        // with our test vector coming from R1CS, v should have length 3
221        assert_eq!(lcccs.v.len(), 3);
222
223        let vec_L_j_x = compute_Ls(&ccs, &lcccs, &z);
224        assert_eq!(vec_L_j_x.len(), lcccs.v.len());
225
226        for (v_i, L_j_x) in lcccs.v.into_iter().zip(vec_L_j_x) {
227            let sum_L_j_x = BooleanHypercube::new(ccs.s)
228                .map(|y| L_j_x.evaluate(&y).unwrap())
229                .fold(Fr::zero(), |acc, result| acc + result);
230            assert_eq!(v_i, sum_L_j_x);
231        }
232    }
233
234    /// Given a bad z, check that the v_j should not match with the L_j(x)
235    #[test]
236    fn test_bad_v_j() {
237        let mut rng = test_rng();
238
239        let ccs = get_test_ccs();
240        let z = get_test_z(3);
241        let (w, x) = ccs.split_z(&z);
242        ccs.check_relation(&w, &x).unwrap();
243
244        // Mutate z so that the relation does not hold
245        let mut bad_z = z.clone();
246        bad_z[3] = Fr::zero();
247        let (bad_w, bad_x) = ccs.split_z(&bad_z);
248        assert!(ccs.check_relation(&bad_w, &bad_x).is_err());
249
250        let (pedersen_params, _) =
251            Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap();
252        // Compute v_j with the right z
253        let (lcccs, _) = ccs
254            .to_lcccs::<_, Projective, Pedersen<Projective>, false>(&mut rng, &pedersen_params, &z)
255            .unwrap();
256        // with our test vector coming from R1CS, v should have length 3
257        assert_eq!(lcccs.v.len(), 3);
258
259        // Bad compute L_j(x) with the bad z
260        let vec_L_j_x = compute_Ls(&ccs, &lcccs, &bad_z);
261        assert_eq!(vec_L_j_x.len(), lcccs.v.len());
262
263        // Make sure that the LCCCS is not satisfied given these L_j(x)
264        // i.e. summing L_j(x) over the hypercube should not give v_j for all j
265        let mut satisfied = true;
266        for (v_i, L_j_x) in lcccs.v.into_iter().zip(vec_L_j_x) {
267            let sum_L_j_x = BooleanHypercube::new(ccs.s)
268                .map(|y| L_j_x.evaluate(&y).unwrap())
269                .fold(Fr::zero(), |acc, result| acc + result);
270            if v_i != sum_L_j_x {
271                satisfied = false;
272            }
273        }
274        assert!(!satisfied);
275    }
276}