arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/folding-schemes/src/folding/nova/nifs/nova.rs
folding-schemes/src/folding/nova/nifs/nova.rsRaw
  1/// This module contains the implementation the NIFSTrait for the
  2/// [Nova](https://eprint.iacr.org/2021/370.pdf) NIFS (Non-Interactive Folding Scheme).
  3use ark_crypto_primitives::sponge::Absorb;
  4use ark_ec::{CurveGroup, Group};
  5use ark_ff::{BigInteger, PrimeField};
  6use ark_std::rand::RngCore;
  7use ark_std::Zero;
  8use std::marker::PhantomData;
  9
 10use super::NIFSTrait;
 11use crate::arith::r1cs::R1CS;
 12use crate::commitment::CommitmentScheme;
 13use crate::folding::circuits::cyclefold::{CycleFoldCommittedInstance, CycleFoldWitness};
 14use crate::folding::nova::circuits::ChallengeGadget;
 15use crate::folding::nova::{CommittedInstance, Witness};
 16use crate::transcript::Transcript;
 17use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, vec_sub};
 18use crate::Error;
 19
 20/// Implements the Non-Interactive Folding Scheme described in section 4 of
 21/// [Nova](https://eprint.iacr.org/2021/370.pdf).
 22/// `H` specifies whether the NIFS will use a blinding factor
 23pub struct NIFS<
 24    C: CurveGroup,
 25    CS: CommitmentScheme<C, H>,
 26    T: Transcript<C::ScalarField>,
 27    const H: bool = false,
 28> {
 29    _c: PhantomData<C>,
 30    _cp: PhantomData<CS>,
 31    _t: PhantomData<T>,
 32}
 33
 34impl<C: CurveGroup, CS: CommitmentScheme<C, H>, T: Transcript<C::ScalarField>, const H: bool>
 35    NIFSTrait<C, CS, T, H> for NIFS<C, CS, T, H>
 36where
 37    <C as Group>::ScalarField: Absorb,
 38    <C as CurveGroup>::BaseField: PrimeField,
 39    <C as Group>::ScalarField: PrimeField,
 40{
 41    type CommittedInstance = CommittedInstance<C>;
 42    type Witness = Witness<C>;
 43    type ProverAux = Vec<C::ScalarField>;
 44    type Proof = C;
 45
 46    fn new_witness(w: Vec<C::ScalarField>, e_len: usize, rng: impl RngCore) -> Self::Witness {
 47        Witness::new::<H>(w, e_len, rng)
 48    }
 49
 50    fn new_instance(
 51        _rng: impl RngCore,
 52        params: &CS::ProverParams,
 53        W: &Self::Witness,
 54        x: Vec<C::ScalarField>,
 55        _aux: Vec<C::ScalarField>,
 56    ) -> Result<Self::CommittedInstance, Error> {
 57        W.commit::<CS, H>(params, x)
 58    }
 59
 60    fn fold_witness(
 61        r: C::ScalarField,
 62        W_i: &Self::Witness,
 63        w_i: &Self::Witness,
 64        aux: &Self::ProverAux, // T in Nova's notation
 65    ) -> Result<Self::Witness, Error> {
 66        let r2 = r * r;
 67        let E: Vec<C::ScalarField> = vec_add(
 68            &vec_add(&W_i.E, &vec_scalar_mul(aux, &r))?, // aux is Nova's T
 69            &vec_scalar_mul(&w_i.E, &r2),
 70        )?;
 71        // use r_T=0 since we don't need hiding property for cm(T)
 72        let rT = C::ScalarField::zero();
 73        let rE = W_i.rE + r * rT + r2 * w_i.rE;
 74        let W: Vec<C::ScalarField> = W_i
 75            .W
 76            .iter()
 77            .zip(&w_i.W)
 78            .map(|(a, b)| *a + (r * b))
 79            .collect();
 80
 81        let rW = W_i.rW + r * w_i.rW;
 82        Ok(Self::Witness { E, rE, W, rW })
 83    }
 84
 85    fn prove(
 86        cs_prover_params: &CS::ProverParams,
 87        r1cs: &R1CS<C::ScalarField>,
 88        transcript: &mut T,
 89        pp_hash: C::ScalarField,
 90        W_i: &Self::Witness,
 91        U_i: &Self::CommittedInstance,
 92        w_i: &Self::Witness,
 93        u_i: &Self::CommittedInstance,
 94    ) -> Result<
 95        (
 96            Self::Witness,
 97            Self::CommittedInstance,
 98            Self::Proof,
 99            Vec<bool>,
100        ),
101        Error,
102    > {
103        // compute the cross terms
104        let z1: Vec<C::ScalarField> = [vec![U_i.u], U_i.x.to_vec(), W_i.W.to_vec()].concat();
105        let z2: Vec<C::ScalarField> = [vec![u_i.u], u_i.x.to_vec(), w_i.W.to_vec()].concat();
106        let T = Self::compute_T(r1cs, U_i.u, u_i.u, &z1, &z2)?;
107
108        // use r_T=0 since we don't need hiding property for cm(T)
109        let cmT = CS::commit(cs_prover_params, &T, &C::ScalarField::zero())?;
110
111        let r_bits = ChallengeGadget::<C, Self::CommittedInstance>::get_challenge_native(
112            transcript,
113            pp_hash,
114            U_i,
115            u_i,
116            Some(&cmT),
117        );
118        let r_Fr = C::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits))
119            .ok_or(Error::OutOfBounds)?;
120
121        let w = Self::fold_witness(r_Fr, W_i, w_i, &T)?;
122
123        let ci = Self::fold_committed_instances(r_Fr, U_i, u_i, &cmT);
124
125        Ok((w, ci, cmT, r_bits))
126    }
127
128    fn verify(
129        transcript: &mut T,
130        pp_hash: C::ScalarField,
131        U_i: &Self::CommittedInstance,
132        u_i: &Self::CommittedInstance,
133        cmT: &C, // Proof
134    ) -> Result<(Self::CommittedInstance, Vec<bool>), Error> {
135        let r_bits = ChallengeGadget::<C, Self::CommittedInstance>::get_challenge_native(
136            transcript,
137            pp_hash,
138            U_i,
139            u_i,
140            Some(cmT),
141        );
142        let r = C::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits))
143            .ok_or(Error::OutOfBounds)?;
144
145        Ok((Self::fold_committed_instances(r, U_i, u_i, cmT), r_bits))
146    }
147}
148
149impl<C: CurveGroup, CS: CommitmentScheme<C, H>, T: Transcript<C::ScalarField>, const H: bool>
150    NIFS<C, CS, T, H>
151where
152    <C as Group>::ScalarField: Absorb,
153    <C as CurveGroup>::BaseField: PrimeField,
154{
155    /// compute_T: compute cross-terms T
156    pub fn compute_T(
157        r1cs: &R1CS<C::ScalarField>,
158        u1: C::ScalarField,
159        u2: C::ScalarField,
160        z1: &[C::ScalarField],
161        z2: &[C::ScalarField],
162    ) -> Result<Vec<C::ScalarField>, Error> {
163        let (A, B, C) = (r1cs.A.clone(), r1cs.B.clone(), r1cs.C.clone());
164
165        // this is parallelizable (for the future)
166        let Az1 = mat_vec_mul(&A, z1)?;
167        let Bz1 = mat_vec_mul(&B, z1)?;
168        let Cz1 = mat_vec_mul(&C, z1)?;
169        let Az2 = mat_vec_mul(&A, z2)?;
170        let Bz2 = mat_vec_mul(&B, z2)?;
171        let Cz2 = mat_vec_mul(&C, z2)?;
172
173        let Az1_Bz2 = hadamard(&Az1, &Bz2)?;
174        let Az2_Bz1 = hadamard(&Az2, &Bz1)?;
175        let u1Cz2 = vec_scalar_mul(&Cz2, &u1);
176        let u2Cz1 = vec_scalar_mul(&Cz1, &u2);
177
178        vec_sub(&vec_sub(&vec_add(&Az1_Bz2, &Az2_Bz1)?, &u1Cz2)?, &u2Cz1)
179    }
180
181    pub fn compute_cyclefold_cmT(
182        cs_prover_params: &CS::ProverParams,
183        r1cs: &R1CS<C::ScalarField>, // R1CS over C2.Fr=C1.Fq (here C=C2)
184        w1: &CycleFoldWitness<C>,
185        ci1: &CycleFoldCommittedInstance<C>,
186        w2: &CycleFoldWitness<C>,
187        ci2: &CycleFoldCommittedInstance<C>,
188    ) -> Result<(Vec<C::ScalarField>, C), Error>
189    where
190        <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
191    {
192        let z1: Vec<C::ScalarField> = [vec![ci1.u], ci1.x.to_vec(), w1.W.to_vec()].concat();
193        let z2: Vec<C::ScalarField> = [vec![ci2.u], ci2.x.to_vec(), w2.W.to_vec()].concat();
194
195        // compute cross terms
196        let T = Self::compute_T(r1cs, ci1.u, ci2.u, &z1, &z2)?;
197        // use r_T=0 since we don't need hiding property for cm(T)
198        let cmT = CS::commit(cs_prover_params, &T, &C::ScalarField::zero())?;
199        Ok((T, cmT))
200    }
201
202    /// folds two committed instances with the given r and cmT. This method is used by
203    /// Nova::verify, but also by Nova::prove and the CycleFoldNIFS::verify.
204    pub fn fold_committed_instances(
205        r: C::ScalarField,
206        U_i: &CommittedInstance<C>,
207        u_i: &CommittedInstance<C>,
208        cmT: &C,
209    ) -> CommittedInstance<C> {
210        let r2 = r * r;
211        let cmE = U_i.cmE + cmT.mul(r) + u_i.cmE.mul(r2);
212        let u = U_i.u + r * u_i.u;
213        let cmW = U_i.cmW + u_i.cmW.mul(r);
214        let x = U_i
215            .x
216            .iter()
217            .zip(&u_i.x)
218            .map(|(a, b)| *a + (r * b))
219            .collect::<Vec<C::ScalarField>>();
220
221        CommittedInstance { cmE, u, cmW, x }
222    }
223
224    pub fn prove_commitments(
225        tr: &mut impl Transcript<C::ScalarField>,
226        cs_prover_params: &CS::ProverParams,
227        w: &Witness<C>,
228        ci: &CommittedInstance<C>,
229        T: Vec<C::ScalarField>,
230        cmT: &C,
231    ) -> Result<[CS::Proof; 3], Error> {
232        let cmE_proof = CS::prove(cs_prover_params, tr, &ci.cmE, &w.E, &w.rE, None)?;
233        let cmW_proof = CS::prove(cs_prover_params, tr, &ci.cmW, &w.W, &w.rW, None)?;
234        let cmT_proof = CS::prove(cs_prover_params, tr, cmT, &T, &C::ScalarField::zero(), None)?; // cm(T) is committed with rT=0
235        Ok([cmE_proof, cmW_proof, cmT_proof])
236    }
237}
238
239#[cfg(test)]
240pub mod tests {
241    use super::*;
242    use ark_crypto_primitives::sponge::poseidon::PoseidonSponge;
243    use ark_pallas::{Fr, Projective};
244
245    use crate::arith::{r1cs::tests::get_test_r1cs, Arith};
246    use crate::commitment::pedersen::Pedersen;
247    use crate::folding::nova::nifs::tests::test_nifs_opt;
248
249    #[test]
250    fn test_nifs_nova() {
251        let (W, U) = test_nifs_opt::<NIFS<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>>();
252
253        // check the last folded instance relation
254        let r1cs = get_test_r1cs();
255        r1cs.check_relation(&W, &U).unwrap();
256    }
257}