Refactor NIFSTrait & port Mova impl to itadapt-preparecalldata
@ -1,134 +0,0 @@ |
|||||
#![allow(unused)]
|
|
||||
use crate::commitment::CommitmentScheme;
|
|
||||
use crate::transcript::AbsorbNonNative;
|
|
||||
use crate::utils::mle::dense_vec_to_dense_mle;
|
|
||||
use crate::utils::vec::is_zero_vec;
|
|
||||
use crate::Error;
|
|
||||
use ark_crypto_primitives::sponge::Absorb;
|
|
||||
use ark_ec::CurveGroup;
|
|
||||
use ark_ff::PrimeField;
|
|
||||
use ark_poly::MultilinearExtension;
|
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
|
|
||||
use ark_std::rand::RngCore;
|
|
||||
|
|
||||
use crate::arith::r1cs::R1CS;
|
|
||||
use crate::folding::circuits::CF1;
|
|
||||
use crate::folding::traits::Dummy;
|
|
||||
use ark_std::{log2, One, UniformRand, Zero};
|
|
||||
|
|
||||
/// Implements the scheme described in [Mova](https://eprint.iacr.org/2024/1220.pdf)
|
|
||||
mod nifs;
|
|
||||
mod pointvsline;
|
|
||||
mod traits;
|
|
||||
|
|
||||
#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
|
|
||||
pub struct CommittedInstance<C: CurveGroup> {
|
|
||||
// Random evaluation point for the E
|
|
||||
pub rE: Vec<C::ScalarField>,
|
|
||||
// Evaluation of the MLE of E at r_E
|
|
||||
pub mleE: C::ScalarField,
|
|
||||
pub u: C::ScalarField,
|
|
||||
pub cmW: C,
|
|
||||
pub x: Vec<C::ScalarField>,
|
|
||||
}
|
|
||||
|
|
||||
/// Witness for the R1CS containing the W vector, the r_w used as randomness for the commitment and the Error term E.
|
|
||||
/// The wi the prover receives in most protocols in the paper.
|
|
||||
#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
|
|
||||
pub struct Witness<C: CurveGroup> {
|
|
||||
pub E: Vec<C::ScalarField>,
|
|
||||
pub W: Vec<C::ScalarField>,
|
|
||||
pub rW: C::ScalarField,
|
|
||||
}
|
|
||||
|
|
||||
/// A helper struct to group together the result of the folded witness and the folded committed instance
|
|
||||
#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
|
|
||||
pub struct InstanceWitness<C: CurveGroup> {
|
|
||||
pub ci: CommittedInstance<C>,
|
|
||||
pub w: Witness<C>,
|
|
||||
}
|
|
||||
|
|
||||
impl<C: CurveGroup> Witness<C> {
|
|
||||
pub fn new<const H: bool>(w: Vec<C::ScalarField>, e_len: usize, mut rng: impl RngCore) -> Self {
|
|
||||
let rW = if H {
|
|
||||
C::ScalarField::rand(&mut rng)
|
|
||||
} else {
|
|
||||
C::ScalarField::zero()
|
|
||||
};
|
|
||||
|
|
||||
Self {
|
|
||||
E: vec![C::ScalarField::zero(); e_len],
|
|
||||
W: w,
|
|
||||
rW,
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
pub fn commit<CS: CommitmentScheme<C>>(
|
|
||||
&self,
|
|
||||
params: &CS::ProverParams,
|
|
||||
x: Vec<C::ScalarField>,
|
|
||||
rE: Vec<C::ScalarField>,
|
|
||||
) -> Result<CommittedInstance<C>, Error> {
|
|
||||
let mut mleE = C::ScalarField::zero();
|
|
||||
if !is_zero_vec::<C::ScalarField>(&self.E) {
|
|
||||
let E = dense_vec_to_dense_mle(log2(self.E.len()) as usize, &self.E);
|
|
||||
mleE = E.evaluate(&rE).ok_or(Error::NotExpectedLength(
|
|
||||
rE.len(),
|
|
||||
log2(self.E.len()) as usize,
|
|
||||
))?;
|
|
||||
}
|
|
||||
let cmW = CS::commit(params, &self.W, &self.rW)?;
|
|
||||
Ok(CommittedInstance {
|
|
||||
rE,
|
|
||||
mleE,
|
|
||||
u: C::ScalarField::one(),
|
|
||||
cmW,
|
|
||||
x,
|
|
||||
})
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl<C: CurveGroup> Dummy<&R1CS<CF1<C>>> for Witness<C> {
|
|
||||
fn dummy(r1cs: &R1CS<CF1<C>>) -> Self {
|
|
||||
Self {
|
|
||||
E: vec![C::ScalarField::zero(); r1cs.A.n_rows],
|
|
||||
W: vec![C::ScalarField::zero(); r1cs.A.n_cols - 1 - r1cs.l],
|
|
||||
rW: C::ScalarField::zero(),
|
|
||||
}
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl<C: CurveGroup> Dummy<usize> for CommittedInstance<C> {
|
|
||||
fn dummy(io_len: usize) -> Self {
|
|
||||
Self {
|
|
||||
rE: vec![C::ScalarField::zero(); io_len],
|
|
||||
mleE: C::ScalarField::zero(),
|
|
||||
u: C::ScalarField::zero(),
|
|
||||
cmW: C::zero(),
|
|
||||
x: vec![C::ScalarField::zero(); io_len],
|
|
||||
}
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl<C: CurveGroup> Absorb for CommittedInstance<C>
|
|
||||
where
|
|
||||
C::ScalarField: Absorb,
|
|
||||
{
|
|
||||
fn to_sponge_bytes(&self, _dest: &mut Vec<u8>) {
|
|
||||
// This is never called
|
|
||||
unimplemented!()
|
|
||||
}
|
|
||||
|
|
||||
fn to_sponge_field_elements<F: PrimeField>(&self, dest: &mut Vec<F>) {
|
|
||||
self.u.to_sponge_field_elements(dest);
|
|
||||
self.x.to_sponge_field_elements(dest);
|
|
||||
self.rE.to_sponge_field_elements(dest);
|
|
||||
self.mleE.to_sponge_field_elements(dest);
|
|
||||
// We cannot call `to_native_sponge_field_elements(dest)` directly, as
|
|
||||
// `to_native_sponge_field_elements` needs `F` to be `C::ScalarField`,
|
|
||||
// but here `F` is a generic `PrimeField`.
|
|
||||
self.cmW
|
|
||||
.to_native_sponge_field_elements_as_vec()
|
|
||||
.to_sponge_field_elements(dest);
|
|
||||
}
|
|
||||
}
|
|
@ -1,440 +0,0 @@ |
|||||
use super::{CommittedInstance, InstanceWitness, Witness};
|
|
||||
use crate::arith::r1cs::R1CS;
|
|
||||
use crate::commitment::CommitmentScheme;
|
|
||||
use crate::folding::mova::pointvsline::{
|
|
||||
PointVsLine, PointVsLineProof, PointvsLineEvaluationClaim,
|
|
||||
};
|
|
||||
use crate::folding::nova::nifs::NIFS as NovaNIFS;
|
|
||||
use crate::transcript::Transcript;
|
|
||||
use crate::utils::mle::dense_vec_to_dense_mle;
|
|
||||
use crate::utils::vec::{vec_add, vec_scalar_mul};
|
|
||||
use crate::Error;
|
|
||||
use ark_crypto_primitives::sponge::Absorb;
|
|
||||
use ark_ec::{CurveGroup, Group};
|
|
||||
use ark_ff::PrimeField;
|
|
||||
use ark_poly::MultilinearExtension;
|
|
||||
use ark_std::log2;
|
|
||||
use std::marker::PhantomData;
|
|
||||
|
|
||||
/// Implements the Non-Interactive Folding Scheme described in section 4 of
|
|
||||
/// [Mova](https://eprint.iacr.org/2024/1220.pdf)
|
|
||||
/// `H` specifies whether the NIFS will use a blinding factor
|
|
||||
pub struct NIFS<
|
|
||||
C: CurveGroup,
|
|
||||
CS: CommitmentScheme<C, H>,
|
|
||||
T: Transcript<C::ScalarField>,
|
|
||||
const H: bool = false,
|
|
||||
> {
|
|
||||
_c: PhantomData<C>,
|
|
||||
_cp: PhantomData<CS>,
|
|
||||
_ct: PhantomData<T>,
|
|
||||
}
|
|
||||
|
|
||||
pub struct Proof<C: CurveGroup> {
|
|
||||
pub h_proof: PointVsLineProof<C>,
|
|
||||
pub mleE1_prime: C::ScalarField,
|
|
||||
pub mleE2_prime: C::ScalarField,
|
|
||||
pub mleT: C::ScalarField,
|
|
||||
}
|
|
||||
|
|
||||
impl<C: CurveGroup, CS: CommitmentScheme<C, H>, T: Transcript<C::ScalarField>, const H: bool>
|
|
||||
NIFS<C, CS, T, H>
|
|
||||
where
|
|
||||
<C as Group>::ScalarField: Absorb,
|
|
||||
<C as CurveGroup>::BaseField: PrimeField,
|
|
||||
{
|
|
||||
// Protocol 7 - point 3 (16)
|
|
||||
pub fn fold_witness(
|
|
||||
a: C::ScalarField,
|
|
||||
w1: &Witness<C>,
|
|
||||
w2: &Witness<C>,
|
|
||||
T: &[C::ScalarField],
|
|
||||
) -> Result<Witness<C>, Error> {
|
|
||||
let a_squared = a * a;
|
|
||||
let E: Vec<C::ScalarField> = vec_add(
|
|
||||
&vec_add(&w1.E, &vec_scalar_mul(T, &a))?,
|
|
||||
&vec_scalar_mul(&w2.E, &a_squared),
|
|
||||
)?;
|
|
||||
let W: Vec<C::ScalarField> =
|
|
||||
w1.W.iter()
|
|
||||
.zip(&w2.W)
|
|
||||
.map(|(i1, i2)| *i1 + (a * i2))
|
|
||||
.collect();
|
|
||||
|
|
||||
let rW = w1.rW + a * w2.rW;
|
|
||||
Ok(Witness::<C> { E, W, rW })
|
|
||||
}
|
|
||||
|
|
||||
// Protocol 7 - point 3 (15)
|
|
||||
pub fn fold_committed_instance(
|
|
||||
a: C::ScalarField,
|
|
||||
ci1: &CommittedInstance<C>,
|
|
||||
ci2: &CommittedInstance<C>,
|
|
||||
rE_prime: Vec<C::ScalarField>,
|
|
||||
mleE1_prime: &C::ScalarField,
|
|
||||
mleE2_prime: &C::ScalarField,
|
|
||||
mleT: &C::ScalarField,
|
|
||||
) -> Result<CommittedInstance<C>, Error> {
|
|
||||
let a_squared = a * a;
|
|
||||
let mleE = *mleE1_prime + a * mleT + a_squared * mleE2_prime;
|
|
||||
let u = ci1.u + a * ci2.u;
|
|
||||
let cmW = ci1.cmW + ci2.cmW.mul(a);
|
|
||||
let x = ci1
|
|
||||
.x
|
|
||||
.iter()
|
|
||||
.zip(&ci2.x)
|
|
||||
.map(|(i1, i2)| *i1 + (a * i2))
|
|
||||
.collect::<Vec<C::ScalarField>>();
|
|
||||
|
|
||||
Ok(CommittedInstance::<C> {
|
|
||||
rE: rE_prime.to_vec(),
|
|
||||
mleE,
|
|
||||
u,
|
|
||||
cmW,
|
|
||||
x,
|
|
||||
})
|
|
||||
}
|
|
||||
|
|
||||
/// [Mova](https://eprint.iacr.org/2024/1220.pdf)'s section 4. Protocol 8
|
|
||||
/// Returns a proof for the pt-vs-line operations along with the folded committed instance
|
|
||||
/// instances and witness
|
|
||||
#[allow(clippy::type_complexity)]
|
|
||||
pub fn prove(
|
|
||||
r1cs: &R1CS<C::ScalarField>,
|
|
||||
transcript: &mut impl Transcript<C::ScalarField>,
|
|
||||
ci1: &CommittedInstance<C>,
|
|
||||
ci2: &CommittedInstance<C>,
|
|
||||
w1: &Witness<C>,
|
|
||||
w2: &Witness<C>,
|
|
||||
) -> Result<(Proof<C>, InstanceWitness<C>), Error> {
|
|
||||
// Protocol 5 is pre-processing
|
|
||||
transcript.absorb(ci1);
|
|
||||
transcript.absorb(ci2);
|
|
||||
|
|
||||
// Protocol 6
|
|
||||
let (
|
|
||||
h_proof,
|
|
||||
PointvsLineEvaluationClaim {
|
|
||||
mleE1_prime,
|
|
||||
mleE2_prime,
|
|
||||
rE_prime,
|
|
||||
},
|
|
||||
) = PointVsLine::<C, T>::prove(transcript, ci1, ci2, w1, w2)?;
|
|
||||
|
|
||||
// Protocol 7
|
|
||||
transcript.absorb(&mleE1_prime);
|
|
||||
transcript.absorb(&mleE2_prime);
|
|
||||
|
|
||||
// Remember Z = (W, x, u)
|
|
||||
let z1: Vec<C::ScalarField> = [vec![ci1.u], ci1.x.to_vec(), w1.W.to_vec()].concat();
|
|
||||
let z2: Vec<C::ScalarField> = [vec![ci2.u], ci2.x.to_vec(), w2.W.to_vec()].concat();
|
|
||||
|
|
||||
let T = NovaNIFS::<C, CS, H>::compute_T(r1cs, ci1.u, ci2.u, &z1, &z2)?;
|
|
||||
|
|
||||
let n_vars: usize = log2(w1.E.len()) as usize;
|
|
||||
if log2(T.len()) as usize != n_vars {
|
|
||||
return Err(Error::NotEqual);
|
|
||||
}
|
|
||||
|
|
||||
let mleT = dense_vec_to_dense_mle(n_vars, &T);
|
|
||||
let mleT_evaluated = mleT.evaluate(&rE_prime).ok_or(Error::EvaluationFail)?;
|
|
||||
|
|
||||
transcript.absorb(&mleT_evaluated);
|
|
||||
|
|
||||
let alpha: C::ScalarField = transcript.get_challenge();
|
|
||||
|
|
||||
Ok((
|
|
||||
Proof::<C> {
|
|
||||
h_proof,
|
|
||||
mleE1_prime,
|
|
||||
mleE2_prime,
|
|
||||
mleT: mleT_evaluated,
|
|
||||
},
|
|
||||
InstanceWitness {
|
|
||||
ci: Self::fold_committed_instance(
|
|
||||
alpha,
|
|
||||
ci1,
|
|
||||
ci2,
|
|
||||
rE_prime,
|
|
||||
&mleE1_prime,
|
|
||||
&mleE2_prime,
|
|
||||
&mleT_evaluated,
|
|
||||
)?,
|
|
||||
w: Self::fold_witness(alpha, w1, w2, &T)?,
|
|
||||
},
|
|
||||
))
|
|
||||
}
|
|
||||
|
|
||||
/// [Mova](https://eprint.iacr.org/2024/1220.pdf)'s section 4.
|
|
||||
/// It verifies the results from both the folding and the pt-vs-line proofs.
|
|
||||
/// Returns the folded committed instance.
|
|
||||
pub fn verify(
|
|
||||
transcript: &mut impl Transcript<C::ScalarField>,
|
|
||||
ci1: &CommittedInstance<C>,
|
|
||||
ci2: &CommittedInstance<C>,
|
|
||||
proof: &Proof<C>,
|
|
||||
) -> Result<CommittedInstance<C>, Error> {
|
|
||||
transcript.absorb(ci1);
|
|
||||
transcript.absorb(ci2);
|
|
||||
let rE_prime = PointVsLine::<C, T>::verify(
|
|
||||
transcript,
|
|
||||
ci1,
|
|
||||
ci2,
|
|
||||
&proof.h_proof,
|
|
||||
&proof.mleE1_prime,
|
|
||||
&proof.mleE2_prime,
|
|
||||
)?;
|
|
||||
|
|
||||
transcript.absorb(&proof.mleE1_prime);
|
|
||||
transcript.absorb(&proof.mleE2_prime);
|
|
||||
transcript.absorb(&proof.mleT);
|
|
||||
|
|
||||
let alpha: C::ScalarField = transcript.get_challenge();
|
|
||||
|
|
||||
Self::fold_committed_instance(
|
|
||||
alpha,
|
|
||||
ci1,
|
|
||||
ci2,
|
|
||||
rE_prime,
|
|
||||
&proof.mleE1_prime,
|
|
||||
&proof.mleE2_prime,
|
|
||||
&proof.mleT,
|
|
||||
)
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
#[cfg(test)]
|
|
||||
pub mod tests {
|
|
||||
use crate::arith::r1cs::tests::{get_test_r1cs, get_test_z};
|
|
||||
use crate::arith::Arith;
|
|
||||
use crate::commitment::pedersen::{Params as PedersenParams, Pedersen};
|
|
||||
use crate::folding::traits::Dummy;
|
|
||||
use crate::transcript::poseidon::poseidon_canonical_config;
|
|
||||
use ark_crypto_primitives::sponge::{
|
|
||||
poseidon::{PoseidonConfig, PoseidonSponge},
|
|
||||
CryptographicSponge,
|
|
||||
};
|
|
||||
use ark_ff::PrimeField;
|
|
||||
use ark_pallas::{Fr, Projective};
|
|
||||
use ark_std::{test_rng, UniformRand, Zero};
|
|
||||
|
|
||||
use super::*;
|
|
||||
|
|
||||
#[allow(clippy::type_complexity)]
|
|
||||
fn prepare_simple_fold_inputs<C>() -> (
|
|
||||
PedersenParams<C>,
|
|
||||
PoseidonConfig<C::ScalarField>,
|
|
||||
R1CS<C::ScalarField>,
|
|
||||
Witness<C>, // w1
|
|
||||
CommittedInstance<C>, // ci1
|
|
||||
Witness<C>, // w2
|
|
||||
CommittedInstance<C>, // ci2
|
|
||||
Proof<C>, // pt-vs-line
|
|
||||
InstanceWitness<C>, // w3, ci3
|
|
||||
)
|
|
||||
where
|
|
||||
C: CurveGroup,
|
|
||||
<C as CurveGroup>::BaseField: PrimeField,
|
|
||||
C::ScalarField: Absorb,
|
|
||||
{
|
|
||||
let r1cs = get_test_r1cs();
|
|
||||
let z1 = get_test_z(3);
|
|
||||
let z2 = get_test_z(4);
|
|
||||
let (w1, x1) = r1cs.split_z(&z1);
|
|
||||
let (w2, x2) = r1cs.split_z(&z2);
|
|
||||
|
|
||||
let mut rng = ark_std::test_rng();
|
|
||||
|
|
||||
let w1 = Witness::<C>::new::<false>(w1.clone(), r1cs.A.n_rows, &mut rng);
|
|
||||
let w2 = Witness::<C>::new::<false>(w2.clone(), r1cs.A.n_rows, &mut rng);
|
|
||||
|
|
||||
let (pedersen_params, _) = Pedersen::<C>::setup(&mut rng, r1cs.A.n_cols).unwrap();
|
|
||||
|
|
||||
// compute committed instances
|
|
||||
let rE_1: Vec<C::ScalarField> = (0..log2(3))
|
|
||||
.map(|_| C::ScalarField::rand(&mut rng))
|
|
||||
.collect();
|
|
||||
let rE_2: Vec<C::ScalarField> = (0..log2(4))
|
|
||||
.map(|_| C::ScalarField::rand(&mut rng))
|
|
||||
.collect();
|
|
||||
let ci1 = w1
|
|
||||
.commit::<Pedersen<C>>(&pedersen_params, x1.clone(), rE_1)
|
|
||||
.unwrap();
|
|
||||
let ci2 = w2
|
|
||||
.commit::<Pedersen<C>>(&pedersen_params, x2.clone(), rE_2)
|
|
||||
.unwrap();
|
|
||||
|
|
||||
let poseidon_config = poseidon_canonical_config::<C::ScalarField>();
|
|
||||
let mut transcript_p = PoseidonSponge::<C::ScalarField>::new(&poseidon_config);
|
|
||||
|
|
||||
let result = NIFS::<C, Pedersen<C>, PoseidonSponge<C::ScalarField>>::prove(
|
|
||||
&r1cs,
|
|
||||
&mut transcript_p,
|
|
||||
&ci1,
|
|
||||
&ci2,
|
|
||||
&w1,
|
|
||||
&w2,
|
|
||||
)
|
|
||||
.unwrap();
|
|
||||
let (proof, instance) = result;
|
|
||||
|
|
||||
(
|
|
||||
pedersen_params,
|
|
||||
poseidon_config,
|
|
||||
r1cs,
|
|
||||
w1,
|
|
||||
ci1,
|
|
||||
w2,
|
|
||||
ci2,
|
|
||||
proof,
|
|
||||
instance,
|
|
||||
)
|
|
||||
}
|
|
||||
|
|
||||
// fold 2 dummy instances and check that the folded instance holds the relaxed R1CS relation
|
|
||||
#[test]
|
|
||||
fn test_nifs_fold_dummy() {
|
|
||||
let r1cs = get_test_r1cs::<Fr>();
|
|
||||
let z1 = get_test_z(3);
|
|
||||
let (w1, x1) = r1cs.split_z(&z1);
|
|
||||
|
|
||||
let mut rng = ark_std::test_rng();
|
|
||||
let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, r1cs.A.n_cols).unwrap();
|
|
||||
|
|
||||
// dummy instance, witness and public inputs zeroes
|
|
||||
let w_dummy = Witness::<Projective>::dummy(&r1cs);
|
|
||||
let mut u_dummy = w_dummy
|
|
||||
.commit::<Pedersen<Projective>>(
|
|
||||
&pedersen_params,
|
|
||||
vec![Fr::zero(); x1.len()],
|
|
||||
vec![Fr::zero(); log2(3) as usize],
|
|
||||
)
|
|
||||
.unwrap();
|
|
||||
u_dummy.u = Fr::zero();
|
|
||||
|
|
||||
let w_i = w_dummy.clone();
|
|
||||
let u_i = u_dummy.clone();
|
|
||||
let W_i = w_dummy.clone();
|
|
||||
let U_i = u_dummy.clone();
|
|
||||
|
|
||||
r1cs.check_relation(&w_i, &u_i).unwrap();
|
|
||||
r1cs.check_relation(&W_i, &U_i).unwrap();
|
|
||||
|
|
||||
let poseidon_config = poseidon_canonical_config::<ark_pallas::Fr>();
|
|
||||
let mut transcript_p: PoseidonSponge<Fr> = PoseidonSponge::<Fr>::new(&poseidon_config);
|
|
||||
|
|
||||
let result = NIFS::<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>::prove(
|
|
||||
&r1cs,
|
|
||||
&mut transcript_p,
|
|
||||
&u_i,
|
|
||||
&U_i,
|
|
||||
&w_i,
|
|
||||
&W_i,
|
|
||||
)
|
|
||||
.unwrap();
|
|
||||
|
|
||||
let (_proof, instance_witness) = result;
|
|
||||
r1cs.check_relation(&instance_witness.w, &instance_witness.ci)
|
|
||||
.unwrap();
|
|
||||
}
|
|
||||
|
|
||||
// fold 2 instances into one
|
|
||||
#[test]
|
|
||||
fn test_nifs_one_fold() {
|
|
||||
let (pedersen_params, poseidon_config, r1cs, w1, ci1, w2, ci2, proof, instance) =
|
|
||||
prepare_simple_fold_inputs::<Projective>();
|
|
||||
|
|
||||
// NIFS.V
|
|
||||
let mut transcript_v: PoseidonSponge<Fr> = PoseidonSponge::<Fr>::new(&poseidon_config);
|
|
||||
let ci3 = NIFS::<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>::verify(
|
|
||||
&mut transcript_v,
|
|
||||
&ci1,
|
|
||||
&ci2,
|
|
||||
&proof,
|
|
||||
)
|
|
||||
.unwrap();
|
|
||||
assert_eq!(ci3, instance.ci);
|
|
||||
|
|
||||
// check that relations hold for the 2 inputted instances and the folded one
|
|
||||
r1cs.check_relation(&w1, &ci1).unwrap();
|
|
||||
r1cs.check_relation(&w2, &ci2).unwrap();
|
|
||||
r1cs.check_relation(&instance.w, &instance.ci).unwrap();
|
|
||||
|
|
||||
// check that folded commitments from folded instance (ci) are equal to folding the
|
|
||||
// use folded rE, rW to commit w3
|
|
||||
let ci3_expected = instance
|
|
||||
.w
|
|
||||
.commit::<Pedersen<Projective>>(&pedersen_params, ci3.x.clone(), instance.ci.rE)
|
|
||||
.unwrap();
|
|
||||
assert_eq!(ci3_expected.cmW, instance.ci.cmW);
|
|
||||
}
|
|
||||
|
|
||||
#[test]
|
|
||||
fn test_nifs_fold_loop() {
|
|
||||
let r1cs = get_test_r1cs();
|
|
||||
let z = get_test_z(3);
|
|
||||
let (w, x) = r1cs.split_z(&z);
|
|
||||
|
|
||||
let mut rng = ark_std::test_rng();
|
|
||||
let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, r1cs.A.n_cols).unwrap();
|
|
||||
|
|
||||
// prepare the running instance
|
|
||||
let rE: Vec<Fr> = (0..log2(3)).map(|_| Fr::rand(&mut rng)).collect();
|
|
||||
let mut running_instance_w =
|
|
||||
Witness::<Projective>::new::<false>(w.clone(), r1cs.A.n_rows, test_rng());
|
|
||||
let mut running_committed_instance = running_instance_w
|
|
||||
.commit::<Pedersen<Projective>>(&pedersen_params, x, rE)
|
|
||||
.unwrap();
|
|
||||
|
|
||||
r1cs.check_relation(&running_instance_w, &running_committed_instance)
|
|
||||
.unwrap();
|
|
||||
|
|
||||
let num_iters = 10;
|
|
||||
for i in 0..num_iters {
|
|
||||
// prepare the incoming instance
|
|
||||
let incoming_instance_z = get_test_z(i + 4);
|
|
||||
let (w, x) = r1cs.split_z(&incoming_instance_z);
|
|
||||
let incoming_instance_w =
|
|
||||
Witness::<Projective>::new::<false>(w.clone(), r1cs.A.n_rows, test_rng());
|
|
||||
let rE: Vec<Fr> = (0..log2(3)).map(|_| Fr::rand(&mut rng)).collect();
|
|
||||
let incoming_committed_instance = incoming_instance_w
|
|
||||
.commit::<Pedersen<Projective>>(&pedersen_params, x, rE)
|
|
||||
.unwrap();
|
|
||||
r1cs.check_relation(&incoming_instance_w, &incoming_committed_instance)
|
|
||||
.unwrap();
|
|
||||
|
|
||||
// NIFS.P
|
|
||||
let poseidon_config = poseidon_canonical_config::<Fr>();
|
|
||||
let mut transcript_p = PoseidonSponge::<Fr>::new(&poseidon_config);
|
|
||||
|
|
||||
let result = NIFS::<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>::prove(
|
|
||||
&r1cs,
|
|
||||
&mut transcript_p,
|
|
||||
&running_committed_instance,
|
|
||||
&incoming_committed_instance,
|
|
||||
&running_instance_w,
|
|
||||
&incoming_instance_w,
|
|
||||
)
|
|
||||
.unwrap();
|
|
||||
|
|
||||
let (proof, instance_witness) = result;
|
|
||||
|
|
||||
// NIFS.V
|
|
||||
let mut transcript_v: PoseidonSponge<Fr> = PoseidonSponge::<Fr>::new(&poseidon_config);
|
|
||||
let _ci3 = NIFS::<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>::verify(
|
|
||||
&mut transcript_v,
|
|
||||
&running_committed_instance,
|
|
||||
&incoming_committed_instance,
|
|
||||
&proof,
|
|
||||
)
|
|
||||
.unwrap();
|
|
||||
|
|
||||
r1cs.check_relation(&instance_witness.w, &instance_witness.ci)
|
|
||||
.unwrap();
|
|
||||
|
|
||||
// set running_instance for next loop iteration
|
|
||||
running_instance_w = instance_witness.w;
|
|
||||
running_committed_instance = instance_witness.ci;
|
|
||||
}
|
|
||||
}
|
|
||||
}
|
|
@ -1,28 +0,0 @@ |
|||||
use crate::arith::ccs::CCS;
|
|
||||
use crate::arith::{r1cs::R1CS, Arith};
|
|
||||
use crate::folding::circuits::CF1;
|
|
||||
use crate::folding::mova::{CommittedInstance, Witness};
|
|
||||
use crate::utils::mle::dense_vec_to_dense_mle;
|
|
||||
use crate::utils::vec::mat_vec_mul;
|
|
||||
use crate::Error;
|
|
||||
use ark_ec::CurveGroup;
|
|
||||
|
|
||||
impl<C: CurveGroup> Arith<Witness<C>, CommittedInstance<C>> for R1CS<CF1<C>> {
|
|
||||
type Evaluation = Vec<CF1<C>>;
|
|
||||
|
|
||||
fn eval_relation(
|
|
||||
&self,
|
|
||||
w: &Witness<C>,
|
|
||||
u: &CommittedInstance<C>,
|
|
||||
) -> Result<Self::Evaluation, Error> {
|
|
||||
self.eval_at_z(&[&[u.u][..], &u.x, &w.W].concat())
|
|
||||
}
|
|
||||
|
|
||||
fn check_evaluation(
|
|
||||
w: &Witness<C>,
|
|
||||
_u: &CommittedInstance<C>,
|
|
||||
e: Self::Evaluation,
|
|
||||
) -> Result<(), Error> {
|
|
||||
(w.E == e).then_some(()).ok_or(Error::NotSatisfied)
|
|
||||
}
|
|
||||
}
|
|
@ -0,0 +1,152 @@ |
|||||
|
/// This module defines the NIFSTrait, which is set to implement the NIFS (Non-Interactive Folding
|
||||
|
/// Scheme) by the various schemes (Nova, Mova, Ova).
|
||||
|
use ark_crypto_primitives::sponge::Absorb;
|
||||
|
use ark_ec::CurveGroup;
|
||||
|
use ark_std::fmt::Debug;
|
||||
|
use ark_std::rand::RngCore;
|
||||
|
|
||||
|
use crate::arith::r1cs::R1CS;
|
||||
|
use crate::commitment::CommitmentScheme;
|
||||
|
use crate::transcript::Transcript;
|
||||
|
use crate::Error;
|
||||
|
|
||||
|
pub mod mova;
|
||||
|
pub mod nova;
|
||||
|
pub mod ova;
|
||||
|
pub mod pointvsline;
|
||||
|
|
||||
|
/// Defines the NIFS (Non-Interactive Folding Scheme) trait, initially defined in
|
||||
|
/// [Nova](https://eprint.iacr.org/2021/370.pdf), and it's variants
|
||||
|
/// [Ova](https://hackmd.io/V4838nnlRKal9ZiTHiGYzw) and
|
||||
|
/// [Mova](https://eprint.iacr.org/2024/1220.pdf).
|
||||
|
/// `H` specifies whether the NIFS will use a blinding factor.
|
||||
|
pub trait NIFSTrait<
|
||||
|
C: CurveGroup,
|
||||
|
CS: CommitmentScheme<C, H>,
|
||||
|
T: Transcript<C::ScalarField>,
|
||||
|
const H: bool = false,
|
||||
|
>
|
||||
|
{
|
||||
|
type CommittedInstance: Debug + Clone + Absorb;
|
||||
|
type Witness: Debug + Clone;
|
||||
|
type ProverAux: Debug + Clone; // Prover's aux params. eg. in Nova is T
|
||||
|
type Proof: Debug + Clone; // proof. eg. in Nova is cmT
|
||||
|
|
||||
|
fn new_witness(w: Vec<C::ScalarField>, e_len: usize, rng: impl RngCore) -> Self::Witness;
|
||||
|
|
||||
|
fn new_instance(
|
||||
|
rng: impl RngCore,
|
||||
|
params: &CS::ProverParams,
|
||||
|
w: &Self::Witness,
|
||||
|
x: Vec<C::ScalarField>,
|
||||
|
aux: Vec<C::ScalarField>, // t_or_e in Ova, empty for Nova
|
||||
|
) -> Result<Self::CommittedInstance, Error>;
|
||||
|
|
||||
|
fn fold_witness(
|
||||
|
r: C::ScalarField,
|
||||
|
W: &Self::Witness, // running witness
|
||||
|
w: &Self::Witness, // incoming witness
|
||||
|
aux: &Self::ProverAux,
|
||||
|
) -> Result<Self::Witness, Error>;
|
||||
|
|
||||
|
/// NIFS.P. Returns a tuple containing the folded Witness, the folded CommittedInstance, and
|
||||
|
/// the used challenge `r` as a vector of bits, so that it can be reused in other methods.
|
||||
|
#[allow(clippy::type_complexity)]
|
||||
|
#[allow(clippy::too_many_arguments)]
|
||||
|
fn prove(
|
||||
|
cs_prover_params: &CS::ProverParams,
|
||||
|
r1cs: &R1CS<C::ScalarField>,
|
||||
|
transcript: &mut T,
|
||||
|
pp_hash: C::ScalarField,
|
||||
|
W_i: &Self::Witness, // running witness
|
||||
|
U_i: &Self::CommittedInstance, // running committed instance
|
||||
|
w_i: &Self::Witness, // incoming witness
|
||||
|
u_i: &Self::CommittedInstance, // incoming committed instance
|
||||
|
) -> Result<
|
||||
|
(
|
||||
|
Self::Witness,
|
||||
|
Self::CommittedInstance,
|
||||
|
Self::Proof,
|
||||
|
Vec<bool>,
|
||||
|
),
|
||||
|
Error,
|
||||
|
>;
|
||||
|
|
||||
|
/// NIFS.V. Returns the folded CommittedInstance and the used challenge `r` as a vector of
|
||||
|
/// bits, so that it can be reused in other methods.
|
||||
|
fn verify(
|
||||
|
transcript: &mut T,
|
||||
|
pp_hash: C::ScalarField,
|
||||
|
U_i: &Self::CommittedInstance,
|
||||
|
u_i: &Self::CommittedInstance,
|
||||
|
proof: &Self::Proof,
|
||||
|
) -> Result<(Self::CommittedInstance, Vec<bool>), Error>;
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
pub mod tests {
|
||||
|
use super::*;
|
||||
|
use crate::transcript::poseidon::poseidon_canonical_config;
|
||||
|
use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge};
|
||||
|
use ark_pallas::{Fr, Projective};
|
||||
|
use ark_std::{test_rng, UniformRand};
|
||||
|
|
||||
|
use super::NIFSTrait;
|
||||
|
use crate::arith::r1cs::tests::{get_test_r1cs, get_test_z};
|
||||
|
use crate::commitment::pedersen::Pedersen;
|
||||
|
|
||||
|
/// Test method used to test the different implementations of the NIFSTrait (ie. Nova, Mova,
|
||||
|
/// Ova). Runs a loop using the NIFS trait, and returns the last Witness and CommittedInstance
|
||||
|
/// so that their relation can be checked.
|
||||
|
pub(crate) fn test_nifs_opt<
|
||||
|
N: NIFSTrait<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>,
|
||||
|
>() -> (N::Witness, N::CommittedInstance) {
|
||||
|
let r1cs = get_test_r1cs();
|
||||
|
let z = get_test_z(3);
|
||||
|
let (w, x) = r1cs.split_z(&z);
|
||||
|
|
||||
|
let mut rng = ark_std::test_rng();
|
||||
|
let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, r1cs.A.n_cols).unwrap();
|
||||
|
|
||||
|
let poseidon_config = poseidon_canonical_config::<Fr>();
|
||||
|
let mut transcript_p = PoseidonSponge::<Fr>::new(&poseidon_config);
|
||||
|
let mut transcript_v = PoseidonSponge::<Fr>::new(&poseidon_config);
|
||||
|
let pp_hash = Fr::rand(&mut rng);
|
||||
|
|
||||
|
// prepare the running instance
|
||||
|
let mut W_i = N::new_witness(w.clone(), r1cs.A.n_rows, test_rng());
|
||||
|
let mut U_i = N::new_instance(&mut rng, &pedersen_params, &W_i, x, vec![]).unwrap();
|
||||
|
|
||||
|
let num_iters = 10;
|
||||
|
for i in 0..num_iters {
|
||||
|
// prepare the incoming instance
|
||||
|
let incoming_instance_z = get_test_z(i + 4);
|
||||
|
let (w, x) = r1cs.split_z(&incoming_instance_z);
|
||||
|
let w_i = N::new_witness(w.clone(), r1cs.A.n_rows, test_rng());
|
||||
|
let u_i = N::new_instance(&mut rng, &pedersen_params, &w_i, x, vec![]).unwrap();
|
||||
|
|
||||
|
// NIFS.P
|
||||
|
let (folded_witness, _, proof, _) = N::prove(
|
||||
|
&pedersen_params,
|
||||
|
&r1cs,
|
||||
|
&mut transcript_p,
|
||||
|
pp_hash,
|
||||
|
&W_i,
|
||||
|
&U_i,
|
||||
|
&w_i,
|
||||
|
&u_i,
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
|
||||
|
// NIFS.V
|
||||
|
let (folded_committed_instance, _) =
|
||||
|
N::verify(&mut transcript_v, pp_hash, &U_i, &u_i, &proof).unwrap();
|
||||
|
|
||||
|
// set running_instance for next loop iteration
|
||||
|
W_i = folded_witness;
|
||||
|
U_i = folded_committed_instance;
|
||||
|
}
|
||||
|
|
||||
|
(W_i, U_i)
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,411 @@ |
|||||
|
/// This module contains the implementation the NIFSTrait for the
|
||||
|
/// [Mova](https://eprint.iacr.org/2024/1220.pdf) NIFS (Non-Interactive Folding Scheme).
|
||||
|
use ark_crypto_primitives::sponge::Absorb;
|
||||
|
use ark_ec::{CurveGroup, Group};
|
||||
|
use ark_ff::PrimeField;
|
||||
|
use ark_poly::MultilinearExtension;
|
||||
|
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
|
||||
|
use ark_std::log2;
|
||||
|
use ark_std::rand::RngCore;
|
||||
|
use ark_std::{One, UniformRand, Zero};
|
||||
|
use std::marker::PhantomData;
|
||||
|
|
||||
|
use super::{
|
||||
|
nova::NIFS as NovaNIFS,
|
||||
|
pointvsline::{PointVsLine, PointVsLineProof, PointvsLineEvaluationClaim},
|
||||
|
NIFSTrait,
|
||||
|
};
|
||||
|
use crate::arith::{r1cs::R1CS, Arith};
|
||||
|
use crate::commitment::CommitmentScheme;
|
||||
|
use crate::folding::circuits::CF1;
|
||||
|
use crate::folding::traits::Dummy;
|
||||
|
use crate::transcript::AbsorbNonNative;
|
||||
|
use crate::transcript::Transcript;
|
||||
|
use crate::utils::{
|
||||
|
mle::dense_vec_to_dense_mle,
|
||||
|
vec::{is_zero_vec, vec_add, vec_scalar_mul},
|
||||
|
};
|
||||
|
use crate::Error;
|
||||
|
|
||||
|
#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
|
||||
|
pub struct CommittedInstance<C: CurveGroup> {
|
||||
|
// Random evaluation point for the E
|
||||
|
pub rE: Vec<C::ScalarField>,
|
||||
|
// mleE is the evaluation of the MLE of E at r_E
|
||||
|
pub mleE: C::ScalarField,
|
||||
|
pub u: C::ScalarField,
|
||||
|
pub cmW: C,
|
||||
|
pub x: Vec<C::ScalarField>,
|
||||
|
}
|
||||
|
|
||||
|
impl<C: CurveGroup> Absorb for CommittedInstance<C>
|
||||
|
where
|
||||
|
C::ScalarField: Absorb,
|
||||
|
{
|
||||
|
fn to_sponge_bytes(&self, _dest: &mut Vec<u8>) {
|
||||
|
// This is never called
|
||||
|
unimplemented!()
|
||||
|
}
|
||||
|
|
||||
|
fn to_sponge_field_elements<F: PrimeField>(&self, dest: &mut Vec<F>) {
|
||||
|
self.u.to_sponge_field_elements(dest);
|
||||
|
self.x.to_sponge_field_elements(dest);
|
||||
|
self.rE.to_sponge_field_elements(dest);
|
||||
|
self.mleE.to_sponge_field_elements(dest);
|
||||
|
// We cannot call `to_native_sponge_field_elements(dest)` directly, as
|
||||
|
// `to_native_sponge_field_elements` needs `F` to be `C::ScalarField`,
|
||||
|
// but here `F` is a generic `PrimeField`.
|
||||
|
self.cmW
|
||||
|
.to_native_sponge_field_elements_as_vec()
|
||||
|
.to_sponge_field_elements(dest);
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<C: CurveGroup> Dummy<usize> for CommittedInstance<C> {
|
||||
|
fn dummy(io_len: usize) -> Self {
|
||||
|
Self {
|
||||
|
rE: vec![C::ScalarField::zero(); io_len],
|
||||
|
mleE: C::ScalarField::zero(),
|
||||
|
u: C::ScalarField::zero(),
|
||||
|
cmW: C::zero(),
|
||||
|
x: vec![C::ScalarField::zero(); io_len],
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
|
||||
|
pub struct Witness<C: CurveGroup> {
|
||||
|
pub E: Vec<C::ScalarField>,
|
||||
|
pub W: Vec<C::ScalarField>,
|
||||
|
pub rW: C::ScalarField,
|
||||
|
}
|
||||
|
|
||||
|
impl<C: CurveGroup> Dummy<&R1CS<C::ScalarField>> for Witness<C> {
|
||||
|
fn dummy(r1cs: &R1CS<C::ScalarField>) -> Self {
|
||||
|
Self {
|
||||
|
E: vec![C::ScalarField::zero(); r1cs.A.n_rows],
|
||||
|
W: vec![C::ScalarField::zero(); r1cs.A.n_cols - 1 - r1cs.l],
|
||||
|
rW: C::ScalarField::zero(),
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<C: CurveGroup> Witness<C> {
|
||||
|
pub fn new<const H: bool>(w: Vec<C::ScalarField>, e_len: usize, mut rng: impl RngCore) -> Self {
|
||||
|
let rW = if H {
|
||||
|
C::ScalarField::rand(&mut rng)
|
||||
|
} else {
|
||||
|
C::ScalarField::zero()
|
||||
|
};
|
||||
|
|
||||
|
Self {
|
||||
|
E: vec![C::ScalarField::zero(); e_len],
|
||||
|
W: w,
|
||||
|
rW,
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub fn commit<CS: CommitmentScheme<C, H>, const H: bool>(
|
||||
|
&self,
|
||||
|
params: &CS::ProverParams,
|
||||
|
x: Vec<C::ScalarField>,
|
||||
|
rE: Vec<C::ScalarField>,
|
||||
|
) -> Result<CommittedInstance<C>, Error> {
|
||||
|
let mut mleE = C::ScalarField::zero();
|
||||
|
if !is_zero_vec::<C::ScalarField>(&self.E) {
|
||||
|
let E = dense_vec_to_dense_mle(log2(self.E.len()) as usize, &self.E);
|
||||
|
mleE = E.evaluate(&rE).ok_or(Error::NotExpectedLength(
|
||||
|
rE.len(),
|
||||
|
log2(self.E.len()) as usize,
|
||||
|
))?;
|
||||
|
}
|
||||
|
let cmW = CS::commit(params, &self.W, &self.rW)?;
|
||||
|
Ok(CommittedInstance {
|
||||
|
rE,
|
||||
|
mleE,
|
||||
|
u: C::ScalarField::one(),
|
||||
|
cmW,
|
||||
|
x,
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
|
||||
|
pub struct Proof<C: CurveGroup> {
|
||||
|
pub h_proof: PointVsLineProof<C>,
|
||||
|
pub mleE1_prime: C::ScalarField,
|
||||
|
pub mleE2_prime: C::ScalarField,
|
||||
|
pub mleT: C::ScalarField,
|
||||
|
}
|
||||
|
|
||||
|
/// Implements the Non-Interactive Folding Scheme described in section 4 of
|
||||
|
/// [Mova](https://eprint.iacr.org/2024/1220.pdf).
|
||||
|
/// `H` specifies whether the NIFS will use a blinding factor
|
||||
|
pub struct NIFS<
|
||||
|
C: CurveGroup,
|
||||
|
CS: CommitmentScheme<C, H>,
|
||||
|
T: Transcript<C::ScalarField>,
|
||||
|
const H: bool = false,
|
||||
|
> {
|
||||
|
_c: PhantomData<C>,
|
||||
|
_cp: PhantomData<CS>,
|
||||
|
_ct: PhantomData<T>,
|
||||
|
}
|
||||
|
|
||||
|
impl<C: CurveGroup, CS: CommitmentScheme<C, H>, T: Transcript<C::ScalarField>, const H: bool>
|
||||
|
NIFSTrait<C, CS, T, H> for NIFS<C, CS, T, H>
|
||||
|
where
|
||||
|
<C as Group>::ScalarField: Absorb,
|
||||
|
<C as CurveGroup>::BaseField: PrimeField,
|
||||
|
{
|
||||
|
type CommittedInstance = CommittedInstance<C>;
|
||||
|
type Witness = Witness<C>;
|
||||
|
type ProverAux = Vec<C::ScalarField>; // T in Mova's notation
|
||||
|
type Proof = Proof<C>;
|
||||
|
|
||||
|
fn new_witness(w: Vec<C::ScalarField>, e_len: usize, rng: impl RngCore) -> Self::Witness {
|
||||
|
Witness::new::<H>(w, e_len, rng)
|
||||
|
}
|
||||
|
|
||||
|
fn new_instance(
|
||||
|
mut rng: impl RngCore,
|
||||
|
params: &CS::ProverParams,
|
||||
|
W: &Self::Witness,
|
||||
|
x: Vec<C::ScalarField>,
|
||||
|
aux: Vec<C::ScalarField>, // = r_E
|
||||
|
) -> Result<Self::CommittedInstance, Error> {
|
||||
|
let mut rE = aux.clone();
|
||||
|
if is_zero_vec(&rE) {
|
||||
|
// means that we're in a fresh instance, so generate random value
|
||||
|
rE = (0..log2(W.E.len()))
|
||||
|
.map(|_| C::ScalarField::rand(&mut rng))
|
||||
|
.collect();
|
||||
|
}
|
||||
|
|
||||
|
W.commit::<CS, H>(params, x, rE)
|
||||
|
}
|
||||
|
|
||||
|
// Protocol 7 - point 3 (16)
|
||||
|
fn fold_witness(
|
||||
|
a: C::ScalarField,
|
||||
|
W_i: &Witness<C>,
|
||||
|
w_i: &Witness<C>,
|
||||
|
aux: &Vec<C::ScalarField>, // T in Mova's notation
|
||||
|
) -> Result<Witness<C>, Error> {
|
||||
|
let a2 = a * a;
|
||||
|
let E: Vec<C::ScalarField> = vec_add(
|
||||
|
&vec_add(&W_i.E, &vec_scalar_mul(aux, &a))?,
|
||||
|
&vec_scalar_mul(&w_i.E, &a2),
|
||||
|
)?;
|
||||
|
let W: Vec<C::ScalarField> = W_i
|
||||
|
.W
|
||||
|
.iter()
|
||||
|
.zip(&w_i.W)
|
||||
|
.map(|(i1, i2)| *i1 + (a * i2))
|
||||
|
.collect();
|
||||
|
|
||||
|
let rW = W_i.rW + a * w_i.rW;
|
||||
|
Ok(Witness::<C> { E, W, rW })
|
||||
|
}
|
||||
|
|
||||
|
/// [Mova](https://eprint.iacr.org/2024/1220.pdf)'s section 4. Protocol 8
|
||||
|
/// Returns a proof for the pt-vs-line operations along with the folded committed instance
|
||||
|
/// instances and witness
|
||||
|
#[allow(clippy::type_complexity)]
|
||||
|
fn prove(
|
||||
|
_cs_prover_params: &CS::ProverParams, // not used in Mova since we don't commit to T
|
||||
|
r1cs: &R1CS<C::ScalarField>,
|
||||
|
transcript: &mut T,
|
||||
|
pp_hash: C::ScalarField,
|
||||
|
W_i: &Witness<C>,
|
||||
|
U_i: &CommittedInstance<C>,
|
||||
|
w_i: &Witness<C>,
|
||||
|
u_i: &CommittedInstance<C>,
|
||||
|
) -> Result<
|
||||
|
(
|
||||
|
Self::Witness,
|
||||
|
Self::CommittedInstance,
|
||||
|
Self::Proof,
|
||||
|
Vec<bool>,
|
||||
|
),
|
||||
|
Error,
|
||||
|
> {
|
||||
|
transcript.absorb(&pp_hash);
|
||||
|
// Protocol 5 is pre-processing
|
||||
|
transcript.absorb(U_i);
|
||||
|
transcript.absorb(u_i);
|
||||
|
|
||||
|
// Protocol 6
|
||||
|
let (
|
||||
|
h_proof,
|
||||
|
PointvsLineEvaluationClaim {
|
||||
|
mleE1_prime,
|
||||
|
mleE2_prime,
|
||||
|
rE_prime,
|
||||
|
},
|
||||
|
) = PointVsLine::<C, T>::prove(transcript, U_i, u_i, W_i, w_i)?;
|
||||
|
|
||||
|
// Protocol 7
|
||||
|
|
||||
|
transcript.absorb(&mleE1_prime);
|
||||
|
transcript.absorb(&mleE2_prime);
|
||||
|
|
||||
|
// compute the cross terms
|
||||
|
let z1: Vec<C::ScalarField> = [vec![U_i.u], U_i.x.to_vec(), W_i.W.to_vec()].concat();
|
||||
|
let z2: Vec<C::ScalarField> = [vec![u_i.u], u_i.x.to_vec(), w_i.W.to_vec()].concat();
|
||||
|
let T = NovaNIFS::<C, CS, T, H>::compute_T(r1cs, U_i.u, u_i.u, &z1, &z2)?;
|
||||
|
|
||||
|
let n_vars: usize = log2(W_i.E.len()) as usize;
|
||||
|
if log2(T.len()) as usize != n_vars {
|
||||
|
return Err(Error::NotExpectedLength(T.len(), n_vars));
|
||||
|
}
|
||||
|
|
||||
|
let mleT = dense_vec_to_dense_mle(n_vars, &T);
|
||||
|
let mleT_evaluated = mleT.evaluate(&rE_prime).ok_or(Error::EvaluationFail)?;
|
||||
|
|
||||
|
transcript.absorb(&mleT_evaluated);
|
||||
|
|
||||
|
let alpha: C::ScalarField = transcript.get_challenge();
|
||||
|
|
||||
|
let ci = Self::fold_committed_instance(
|
||||
|
alpha,
|
||||
|
U_i,
|
||||
|
u_i,
|
||||
|
&rE_prime,
|
||||
|
&mleE1_prime,
|
||||
|
&mleE2_prime,
|
||||
|
&mleT_evaluated,
|
||||
|
)?;
|
||||
|
let w = Self::fold_witness(alpha, W_i, w_i, &T)?;
|
||||
|
|
||||
|
let proof = Self::Proof {
|
||||
|
h_proof,
|
||||
|
mleE1_prime,
|
||||
|
mleE2_prime,
|
||||
|
mleT: mleT_evaluated,
|
||||
|
};
|
||||
|
Ok((
|
||||
|
w,
|
||||
|
ci,
|
||||
|
proof,
|
||||
|
vec![], // r_bits, returned to be passed as inputs to the circuit, not used at the
|
||||
|
// current impl status
|
||||
|
))
|
||||
|
}
|
||||
|
|
||||
|
/// [Mova](https://eprint.iacr.org/2024/1220.pdf)'s section 4. It verifies the results from the proof
|
||||
|
/// Both the folding and the pt-vs-line proof
|
||||
|
/// returns the folded committed instance
|
||||
|
fn verify(
|
||||
|
transcript: &mut T,
|
||||
|
pp_hash: C::ScalarField,
|
||||
|
U_i: &CommittedInstance<C>,
|
||||
|
u_i: &CommittedInstance<C>,
|
||||
|
proof: &Proof<C>,
|
||||
|
) -> Result<(Self::CommittedInstance, Vec<bool>), Error> {
|
||||
|
transcript.absorb(&pp_hash);
|
||||
|
transcript.absorb(U_i);
|
||||
|
transcript.absorb(u_i);
|
||||
|
let rE_prime = PointVsLine::<C, T>::verify(
|
||||
|
transcript,
|
||||
|
U_i,
|
||||
|
u_i,
|
||||
|
&proof.h_proof,
|
||||
|
&proof.mleE1_prime,
|
||||
|
&proof.mleE2_prime,
|
||||
|
)?;
|
||||
|
|
||||
|
transcript.absorb(&proof.mleE1_prime);
|
||||
|
transcript.absorb(&proof.mleE2_prime);
|
||||
|
transcript.absorb(&proof.mleT);
|
||||
|
|
||||
|
let alpha: C::ScalarField = transcript.get_challenge();
|
||||
|
|
||||
|
Ok((
|
||||
|
Self::fold_committed_instance(
|
||||
|
alpha,
|
||||
|
U_i,
|
||||
|
u_i,
|
||||
|
&rE_prime,
|
||||
|
&proof.mleE1_prime,
|
||||
|
&proof.mleE2_prime,
|
||||
|
&proof.mleT,
|
||||
|
)?,
|
||||
|
vec![],
|
||||
|
))
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<C: CurveGroup, CS: CommitmentScheme<C, H>, T: Transcript<C::ScalarField>, const H: bool>
|
||||
|
NIFS<C, CS, T, H>
|
||||
|
{
|
||||
|
// Protocol 7 - point 3 (15)
|
||||
|
fn fold_committed_instance(
|
||||
|
a: C::ScalarField,
|
||||
|
U_i: &CommittedInstance<C>,
|
||||
|
u_i: &CommittedInstance<C>,
|
||||
|
rE_prime: &[C::ScalarField],
|
||||
|
mleE1_prime: &C::ScalarField,
|
||||
|
mleE2_prime: &C::ScalarField,
|
||||
|
mleT: &C::ScalarField,
|
||||
|
) -> Result<CommittedInstance<C>, Error> {
|
||||
|
let a2 = a * a;
|
||||
|
let mleE = *mleE1_prime + a * mleT + a2 * mleE2_prime;
|
||||
|
let u = U_i.u + a * u_i.u;
|
||||
|
let cmW = U_i.cmW + u_i.cmW.mul(a);
|
||||
|
let x = U_i
|
||||
|
.x
|
||||
|
.iter()
|
||||
|
.zip(&u_i.x)
|
||||
|
.map(|(i1, i2)| *i1 + (a * i2))
|
||||
|
.collect::<Vec<C::ScalarField>>();
|
||||
|
|
||||
|
Ok(CommittedInstance::<C> {
|
||||
|
rE: rE_prime.to_vec(),
|
||||
|
mleE,
|
||||
|
u,
|
||||
|
cmW,
|
||||
|
x,
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<C: CurveGroup> Arith<Witness<C>, CommittedInstance<C>> for R1CS<CF1<C>> {
|
||||
|
type Evaluation = Vec<CF1<C>>;
|
||||
|
|
||||
|
fn eval_relation(
|
||||
|
&self,
|
||||
|
w: &Witness<C>,
|
||||
|
u: &CommittedInstance<C>,
|
||||
|
) -> Result<Self::Evaluation, Error> {
|
||||
|
self.eval_at_z(&[&[u.u][..], &u.x, &w.W].concat())
|
||||
|
}
|
||||
|
|
||||
|
fn check_evaluation(
|
||||
|
w: &Witness<C>,
|
||||
|
_u: &CommittedInstance<C>,
|
||||
|
e: Self::Evaluation,
|
||||
|
) -> Result<(), Error> {
|
||||
|
(w.E == e).then_some(()).ok_or(Error::NotSatisfied)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
pub mod tests {
|
||||
|
use super::*;
|
||||
|
use ark_crypto_primitives::sponge::poseidon::PoseidonSponge;
|
||||
|
use ark_pallas::{Fr, Projective};
|
||||
|
|
||||
|
use crate::arith::{r1cs::tests::get_test_r1cs, Arith};
|
||||
|
use crate::commitment::pedersen::Pedersen;
|
||||
|
use crate::folding::nova::nifs::tests::test_nifs_opt;
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_nifs_mova() {
|
||||
|
let (W, U) = test_nifs_opt::<NIFS<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>>();
|
||||
|
|
||||
|
// check the last folded instance relation
|
||||
|
let r1cs = get_test_r1cs();
|
||||
|
r1cs.check_relation(&W, &U).unwrap();
|
||||
|
}
|
||||
|
}
|