arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/folding-schemes/src/folding/nova/nifs/pointvsline.rs
folding-schemes/src/folding/nova/nifs/pointvsline.rsRaw
  1use ark_crypto_primitives::sponge::Absorb;
  2use ark_ec::{CurveGroup, Group};
  3use ark_ff::{One, PrimeField};
  4use ark_poly::univariate::DensePolynomial;
  5use ark_poly::{DenseMultilinearExtension, DenseUVPolynomial, Polynomial};
  6use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
  7use ark_std::{log2, Zero};
  8
  9use super::mova::{CommittedInstance, Witness};
 10use crate::transcript::Transcript;
 11use crate::utils::mle::dense_vec_to_dense_mle;
 12use crate::Error;
 13
 14/// Implements the Points vs Line as described in
 15/// [Mova](https://eprint.iacr.org/2024/1220.pdf) and Section 4.5.2 from Thaler’s book
 16
 17/// Claim from step 3 protocol 6
 18pub struct PointvsLineEvaluationClaim<C: CurveGroup> {
 19    pub mleE1_prime: C::ScalarField,
 20    pub mleE2_prime: C::ScalarField,
 21    pub rE_prime: Vec<C::ScalarField>,
 22}
 23/// Proof from step 1 protocol 6
 24#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
 25pub struct PointVsLineProof<C: CurveGroup> {
 26    pub h1: DensePolynomial<C::ScalarField>,
 27    pub h2: DensePolynomial<C::ScalarField>,
 28}
 29
 30#[derive(Clone, Debug, Default)]
 31pub struct PointVsLine<C: CurveGroup, T: Transcript<C::ScalarField>> {
 32    _phantom_C: std::marker::PhantomData<C>,
 33    _phantom_T: std::marker::PhantomData<T>,
 34}
 35
 36/// Protocol 6 from Mova
 37impl<C: CurveGroup, T: Transcript<C::ScalarField>> PointVsLine<C, T>
 38where
 39    <C as Group>::ScalarField: Absorb,
 40{
 41    pub fn prove(
 42        transcript: &mut T,
 43        ci1: &CommittedInstance<C>,
 44        ci2: &CommittedInstance<C>,
 45        w1: &Witness<C>,
 46        w2: &Witness<C>,
 47    ) -> Result<(PointVsLineProof<C>, PointvsLineEvaluationClaim<C>), Error> {
 48        let n_vars: usize = log2(w1.E.len()) as usize;
 49
 50        let mleE1 = dense_vec_to_dense_mle(n_vars, &w1.E);
 51        let mleE2 = dense_vec_to_dense_mle(n_vars, &w2.E);
 52
 53        // We have l(0) = r1, l(1) = r2 so we know that l(x) = r1 + x(r2-r1) thats why we need r2-r1
 54        let r1_sub_r2: Vec<<C>::ScalarField> = ci1
 55            .rE
 56            .iter()
 57            .zip(&ci2.rE)
 58            .map(|(&r1, r2)| r1 - r2)
 59            .collect();
 60
 61        let h1 = compute_h(&mleE1, &ci1.rE, &r1_sub_r2)?;
 62        let h2 = compute_h(&mleE2, &ci1.rE, &r1_sub_r2)?;
 63
 64        transcript.absorb(&h1.coeffs());
 65        transcript.absorb(&h2.coeffs());
 66
 67        let beta_scalar = C::ScalarField::from_le_bytes_mod_order(b"beta");
 68        transcript.absorb(&beta_scalar);
 69        let beta = transcript.get_challenge();
 70
 71        let mleE1_prime = h1.evaluate(&beta);
 72        let mleE2_prime = h2.evaluate(&beta);
 73
 74        let rE_prime = compute_l(&ci1.rE, &r1_sub_r2, beta)?;
 75
 76        Ok((
 77            PointVsLineProof { h1, h2 },
 78            PointvsLineEvaluationClaim {
 79                mleE1_prime,
 80                mleE2_prime,
 81                rE_prime,
 82            },
 83        ))
 84    }
 85
 86    pub fn verify(
 87        transcript: &mut T,
 88        ci1: &CommittedInstance<C>,
 89        ci2: &CommittedInstance<C>,
 90        proof: &PointVsLineProof<C>,
 91        mleE1_prime: &<C>::ScalarField,
 92        mleE2_prime: &<C>::ScalarField,
 93    ) -> Result<
 94        Vec<<C>::ScalarField>, // rE=rE1'=rE2'.
 95        Error,
 96    > {
 97        if proof.h1.evaluate(&C::ScalarField::zero()) != ci1.mleE {
 98            return Err(Error::NotEqual);
 99        }
100
101        if proof.h2.evaluate(&C::ScalarField::one()) != ci2.mleE {
102            return Err(Error::NotEqual);
103        }
104
105        transcript.absorb(&proof.h1.coeffs());
106        transcript.absorb(&proof.h2.coeffs());
107
108        let beta_scalar = C::ScalarField::from_le_bytes_mod_order(b"beta");
109        transcript.absorb(&beta_scalar);
110        let beta = transcript.get_challenge();
111
112        if *mleE1_prime != proof.h1.evaluate(&beta) {
113            return Err(Error::NotEqual);
114        }
115
116        if *mleE2_prime != proof.h2.evaluate(&beta) {
117            return Err(Error::NotEqual);
118        }
119
120        let r1_sub_r2: Vec<<C>::ScalarField> = ci1
121            .rE
122            .iter()
123            .zip(&ci2.rE)
124            .map(|(&r1, r2)| r1 - r2)
125            .collect();
126        let rE_prime = compute_l(&ci1.rE, &r1_sub_r2, beta)?;
127
128        Ok(rE_prime)
129    }
130}
131
132fn compute_h<F: PrimeField>(
133    mle: &DenseMultilinearExtension<F>,
134    r1: &[F],
135    r1_sub_r2: &[F],
136) -> Result<DensePolynomial<F>, Error> {
137    let n_vars: usize = mle.num_vars;
138    if r1.len() != r1_sub_r2.len() || r1.len() != n_vars {
139        return Err(Error::NotEqual);
140    }
141
142    // Initialize the polynomial vector from the evaluations in the multilinear extension.
143    // Each evaluation is turned into a constant polynomial.
144    let mut poly: Vec<DensePolynomial<F>> = mle
145        .evaluations
146        .iter()
147        .map(|&x| DensePolynomial::from_coefficients_slice(&[x]))
148        .collect();
149
150    for (i, (&r1_i, &r1_sub_r2_i)) in r1.iter().zip(r1_sub_r2.iter()).enumerate().take(n_vars) {
151        // Create a linear polynomial r(X) = r1_i + (r1_sub_r2_i) * X (basically l)
152        let r = DensePolynomial::from_coefficients_slice(&[r1_i, r1_sub_r2_i]);
153        let half_len = 1 << (n_vars - i - 1);
154
155        for b in 0..half_len {
156            let left = &poly[b << 1];
157            let right = &poly[(b << 1) + 1];
158            poly[b] = left + &(&r * &(right - left));
159        }
160    }
161
162    // After the loop, we should be left with a single polynomial, so return it.
163    Ok(poly.swap_remove(0))
164}
165
166fn compute_l<F: PrimeField>(r1: &[F], r1_sub_r2: &[F], x: F) -> Result<Vec<F>, Error> {
167    if r1.len() != r1_sub_r2.len() {
168        return Err(Error::NotEqual);
169    }
170
171    // we have l(x) = r1 + x(r2-r1) so return the result
172    Ok(r1
173        .iter()
174        .zip(r1_sub_r2)
175        .map(|(&r1, &r1_sub_r0)| r1 + x * r1_sub_r0)
176        .collect())
177}
178
179#[cfg(test)]
180mod tests {
181    use super::{compute_h, compute_l};
182    use ark_pallas::Fq;
183    use ark_poly::{DenseMultilinearExtension, DenseUVPolynomial};
184
185    #[test]
186    fn test_compute_h() {
187        let mle = DenseMultilinearExtension::from_evaluations_slice(1, &[Fq::from(1), Fq::from(2)]);
188        let r0 = [Fq::from(5)];
189        let r1 = [Fq::from(6)];
190        let r1_sub_r0: Vec<Fq> = r1.iter().zip(&r0).map(|(&x, y)| x - y).collect();
191
192        let result = compute_h(&mle, &r0, &r1_sub_r0).unwrap();
193        assert_eq!(
194            result,
195            DenseUVPolynomial::from_coefficients_slice(&[Fq::from(6), Fq::from(1)])
196        );
197
198        let mle = DenseMultilinearExtension::from_evaluations_slice(1, &[Fq::from(1), Fq::from(2)]);
199        let r0 = [Fq::from(4)];
200        let r1 = [Fq::from(7)];
201        let r1_sub_r0: Vec<Fq> = r1.iter().zip(&r0).map(|(&x, y)| x - y).collect();
202
203        let result = compute_h(&mle, &r0, &r1_sub_r0).unwrap();
204        assert_eq!(
205            result,
206            DenseUVPolynomial::from_coefficients_slice(&[Fq::from(5), Fq::from(3)])
207        );
208
209        let mle = DenseMultilinearExtension::from_evaluations_slice(
210            2,
211            &[Fq::from(1), Fq::from(2), Fq::from(3), Fq::from(4)],
212        );
213        let r0 = [Fq::from(5), Fq::from(4)];
214        let r1 = [Fq::from(2), Fq::from(7)];
215        let r1_sub_r0: Vec<Fq> = r1.iter().zip(&r0).map(|(&x, y)| x - y).collect();
216
217        let result = compute_h(&mle, &r0, &r1_sub_r0).unwrap();
218        assert_eq!(
219            result,
220            DenseUVPolynomial::from_coefficients_slice(&[Fq::from(14), Fq::from(3)])
221        );
222        let mle = DenseMultilinearExtension::from_evaluations_slice(
223            3,
224            &[
225                Fq::from(1),
226                Fq::from(2),
227                Fq::from(3),
228                Fq::from(4),
229                Fq::from(5),
230                Fq::from(6),
231                Fq::from(7),
232                Fq::from(8),
233            ],
234        );
235        let r0 = [Fq::from(1), Fq::from(2), Fq::from(3)];
236        let r1 = [Fq::from(5), Fq::from(6), Fq::from(7)];
237        let r1_sub_r0: Vec<Fq> = r1.iter().zip(&r0).map(|(&x, y)| x - y).collect();
238
239        let result = compute_h(&mle, &r0, &r1_sub_r0).unwrap();
240        assert_eq!(
241            result,
242            DenseUVPolynomial::from_coefficients_slice(&[Fq::from(18), Fq::from(28)])
243        );
244    }
245
246    #[test]
247    fn test_compute_h_errors() {
248        let mle = DenseMultilinearExtension::from_evaluations_slice(1, &[Fq::from(1), Fq::from(2)]);
249        let r0 = [Fq::from(5)];
250        let r1_sub_r0 = [];
251        let result = compute_h(&mle, &r0, &r1_sub_r0);
252        assert!(result.is_err());
253
254        let mle = DenseMultilinearExtension::from_evaluations_slice(
255            2,
256            &[Fq::from(1), Fq::from(2), Fq::from(1), Fq::from(2)],
257        );
258        let r0 = [Fq::from(4)];
259        let r1 = [Fq::from(7)];
260        let r1_sub_r0: Vec<Fq> = r1.iter().zip(&r0).map(|(&x, y)| x - y).collect();
261
262        let result = compute_h(&mle, &r0, &r1_sub_r0);
263        assert!(result.is_err())
264    }
265
266    #[test]
267    fn test_compute_l() {
268        // Test with simple non-zero values
269        let r1 = vec![Fq::from(1), Fq::from(2), Fq::from(3)];
270        let r1_sub_r2 = vec![Fq::from(4), Fq::from(5), Fq::from(6)];
271        let x = Fq::from(2);
272
273        let expected = vec![
274            Fq::from(1) + Fq::from(2) * Fq::from(4),
275            Fq::from(2) + Fq::from(2) * Fq::from(5),
276            Fq::from(3) + Fq::from(2) * Fq::from(6),
277        ];
278
279        let result = compute_l(&r1, &r1_sub_r2, x).unwrap();
280        assert_eq!(result, expected);
281    }
282}