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