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