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