* refactor NIFSTrait interface to fit Nova variants (Nova,Mova,Ova) Refactor NIFSTrait interface to fit Nova variants (Nova,Mova,Ova). The relevant change is instead of passing the challenge as input, now it passes the transcript and computes the challenges internally (Nova & Ova still compute a single challenge, but Mova computes multiple while absorbing at different steps). * port Mova impl to the NIFSTrait * remove unnecessary wrappers in the nova/zk.rs * remove Nova NIFS methods that are no longer needed after the refactor * put together the different NIFS implementations (Nova, Mova, Ova) so that they can interchanged at usage. The idea is that Nova and its variants (Ova & Mova) share most of the logic for the circuits & IVC & Deciders, so with the abstracted NIFS interface we will be able to reuse most of the already existing Nova code for having the Mova & Ova circuits, IVC, and Decider.update-nifs-interface
@ -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();
|
|||
}
|
|||
}
|