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();
|
|||
}
|
|||
}
|