Browse Source

add initial IVC proof generation impl

ivc-proofs
arnaucube 1 year ago
parent
commit
87671770e7
5 changed files with 164 additions and 17 deletions
  1. +1
    -1
      README.md
  2. +24
    -12
      src/circuits.rs
  3. +134
    -0
      src/ivc.rs
  4. +1
    -0
      src/lib.rs
  5. +4
    -4
      src/nifs.rs

+ 1
- 1
README.md

@ -11,4 +11,4 @@ Thanks to [Levs57](https://twitter.com/levs57), [Nalin Bhardwaj](https://twitter
### Details
_"Nova: Recursive Zero-Knowledge Arguments from Folding Schemes"_ (https://eprint.iacr.org/2021/370) by [Abhiram Kothapalli](https://twitter.com/abhiramko/), [Srinath Setty](https://twitter.com/srinathtv/), [Ioanna Tzialla](https://scholar.google.com/citations?user=IXTY4MYAAAAJ).
Current implementation uses a cycle of pairing-friendly curves with Groth16 for the folding proofs (as an example, the tests use MNT4, MNT6 curves), once the full scheme works, will see how many constraints the folding circuits need and might change one of the sides to a non-pairing curve with a non-pairing proof instead of Groth16. Eventually would like to explore also using BN254 with Grumpkin for ending up verifying the proofs in Ethereum.
Current implementation uses a cycle of pairing-friendly curves with Groth16 for the compressed IVC proofs (as an example, the tests use MNT4, MNT6 curves), once the full scheme works, will see how many constraints the circuits need and might change one of the sides to a non-pairing curve with a non-pairing proof instead of Groth16. Eventually would like to explore also using BN254 with Grumpkin for ending up verifying the proofs in Ethereum.

+ 24
- 12
src/circuits.rs

@ -125,17 +125,18 @@ pub struct AugmentedFCircuit>>
where
<<C as CurveGroup>::BaseField as Field>::BasePrimeField: Absorb,
{
_c: PhantomData<C>,
_gc: PhantomData<GC>,
pub _c: PhantomData<C>,
pub _gc: PhantomData<GC>,
pub poseidon_native: PoseidonSponge<ConstraintF<C>>,
// pub poseidon_native: PoseidonSponge<ConstraintF<C>>,
pub poseidon_config: PoseidonConfig<ConstraintF<C>>,
pub i: Option<C::BaseField>,
pub z_0: Option<C::BaseField>,
pub z_i: Option<C::BaseField>,
pub phi: Option<Phi<C>>, // phi in the paper sometimes appears as phi (φ) and others as 𝗎
pub phiBig: Option<Phi<C>>,
pub phiOut: Option<Phi<C>>,
pub z_i1: Option<C::BaseField>, // z_{i+1}
pub phi: Option<Phi<C>>, // phi_i in the paper sometimes appears as phi (φ) and others as 𝗎
pub phiBig: Option<Phi<C>>, // ϕ_i
pub phiOut: Option<Phi<C>>, // ϕ_{i+1}
pub cmT: Option<C>,
pub r: Option<C::ScalarField>, // This will not be an input and derived from a hash internally in the circuit (poseidon transcript)
}
@ -156,6 +157,7 @@ where
let i = FpVar::<ConstraintF<C>>::new_witness(cs.clone(), || Ok(self.i.unwrap()))?;
let z_0 = FpVar::<ConstraintF<C>>::new_witness(cs.clone(), || Ok(self.z_0.unwrap()))?;
let z_i = FpVar::<ConstraintF<C>>::new_witness(cs.clone(), || Ok(self.z_i.unwrap()))?;
let z_i1 = FpVar::<ConstraintF<C>>::new_witness(cs.clone(), || Ok(self.z_i1.unwrap()))?;
let phi = PhiVar::<C, GC>::new_witness(cs.clone(), || Ok(self.phi.unwrap()))?;
let phiBig = PhiVar::<C, GC>::new_witness(cs.clone(), || Ok(self.phiBig.unwrap()))?;
@ -168,8 +170,9 @@ where
})?; // r will come from transcript
// 1. phi.x == H(vk_nifs, i, z_0, z_i, phiBig)
let mut sponge = PoseidonSpongeVar::<ConstraintF<C>>::new(cs, &self.poseidon_config);
let input = vec![i, z_0, z_i];
let mut sponge =
PoseidonSpongeVar::<ConstraintF<C>>::new(cs.clone(), &self.poseidon_config);
let input = vec![i.clone(), z_0.clone(), z_i.clone()];
sponge.absorb(&input)?;
let input = vec![
phiBig.u.to_constraint_field()?,
@ -182,15 +185,24 @@ where
let x_CF = phi.x.to_constraint_field()?; // phi.x on the ConstraintF<C>
x_CF[0].enforce_equal(&h[0])?; // review
// // 2. phi.cmE==0, phi.u==1
// <GC as CurveVar<C, ConstraintF<C>>>::is_zero(&phi.cmE)?;
// 2. phi.cmE==0, phi.u==1
(phi.cmE.is_zero()?).enforce_equal(&Boolean::TRUE)?;
(phi.u.is_one()?).enforce_equal(&Boolean::TRUE)?;
// 3. nifs.verify
NIFSGadget::<C, GC>::verify(r, cmT, phi, phiBig, phiOut)?;
// 3. nifs.verify, checks that folding phi & phiBig obtains phiOut
NIFSGadget::<C, GC>::verify(r, cmT, phi, phiBig, phiOut.clone())?;
// 4. zksnark.V(vk_snark, phi_new, proof_phi)
// 5. phiOut.x == H(i+1, z_0, z_i+1, phiOut)
// WIP
let mut sponge = PoseidonSpongeVar::<ConstraintF<C>>::new(cs, &self.poseidon_config);
let input = vec![i + FpVar::<ConstraintF<C>>::one(), z_0, z_i1];
sponge.absorb(&input)?;
let input = vec![phiOut.cmE.to_bytes()?, phiOut.cmW.to_bytes()?];
sponge.absorb(&input)?;
let h = sponge.squeeze_field_elements(1).unwrap();
let x_CF = phiOut.x.to_constraint_field()?; // phi.x on the ConstraintF<C>
x_CF[0].enforce_equal(&h[0])?; // review
Ok(())
}

+ 134
- 0
src/ivc.rs

@ -0,0 +1,134 @@
use ark_crypto_primitives::sponge::Absorb;
use ark_ec::{CurveGroup, Group};
use ark_ff::{Field, PrimeField};
use std::marker::PhantomData;
use ark_crypto_primitives::sponge::poseidon::{PoseidonConfig, PoseidonSponge};
use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar};
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError};
use crate::circuits::{AugmentedFCircuit, ConstraintF};
use crate::nifs::{FWit, Phi, NIFS, R1CS};
use crate::pedersen::{Commitment, Params as PedersenParams, Pedersen, Proof as PedersenProof};
use crate::transcript::Transcript;
use ark_std::{One, Zero};
use core::ops::Deref;
pub struct IVCProof<C1: CurveGroup, C2: CurveGroup> {
phi: Phi<C1>,
// w: FWit<C1>,
w: Vec<C1::ScalarField>,
phiBig: Phi<C2>,
// W: FWit<C2>,
W: Vec<C2::ScalarField>,
}
pub struct IVC<
C1: CurveGroup,
GC1: CurveVar<C1, ConstraintF<C1>>,
C2: CurveGroup,
GC2: CurveVar<C2, ConstraintF<C2>>,
> where
C1: CurveGroup<BaseField = <C2 as Group>::ScalarField>,
C2: CurveGroup<BaseField = <C1 as Group>::ScalarField>,
{
_c1: PhantomData<C1>,
_gc1: PhantomData<GC1>,
_c2: PhantomData<C2>,
_gc2: PhantomData<GC2>,
pub poseidon_config: PoseidonConfig<ConstraintF<C2>>,
pub pedersen_params_C1: PedersenParams<C1>,
pub pedersen_params_C2: PedersenParams<C2>,
}
impl<
C1: CurveGroup,
GC1: CurveVar<C1, ConstraintF<C1>>,
C2: CurveGroup,
GC2: CurveVar<C2, ConstraintF<C2>>,
> IVC<C1, GC1, C2, GC2>
where
for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>,
for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
C1: CurveGroup<BaseField = <C2 as Group>::ScalarField>,
C2: CurveGroup<BaseField = <C1 as Group>::ScalarField>,
<C1 as Group>::ScalarField: Absorb,
<C1 as CurveGroup>::BaseField: Absorb,
<<C1 as CurveGroup>::BaseField as Field>::BasePrimeField: Absorb,
<C1 as CurveGroup>::BaseField: PrimeField,
// 2
<C2 as Group>::ScalarField: Absorb,
<C2 as CurveGroup>::BaseField: Absorb,
<<C2 as CurveGroup>::BaseField as Field>::BasePrimeField: Absorb,
<C2 as CurveGroup>::BaseField: PrimeField,
{
pub fn prove(
&self,
cs: ConstraintSystemRef<ConstraintF<C2>>,
tr1: &mut Transcript<C1::ScalarField, C1>,
tr2: &mut Transcript<C2::ScalarField, C2>,
r1cs: &R1CS<C2::ScalarField>,
i: C1::ScalarField,
z_0: C1::ScalarField,
z_i: C1::ScalarField,
// phi1: &Phi<C>,
// phi2: &Phi<C>,
fw1: FWit<C2>,
fw2: FWit<C2>,
) -> Result<IVCProof<C1, C2>, SynthesisError> {
tr1.get_challenge();
let r = tr2.get_challenge(); // TODO transcript usage is still WIP, will update with expected adds & gets
// fold phi_i and phiBig_i
let (fw3, phi1, phi2, _T, cmT) =
NIFS::<C2>::P(tr2, &self.pedersen_params_C2, r, r1cs, fw1, fw2);
let phi3 = NIFS::<C2>::V(r, &phi1, &phi2, &cmT);
// TODO compute z_{i+1}
let z_i1 = z_i.clone(); // WIP this will be the actual computed z_{i+1}
let c = AugmentedFCircuit::<C2, GC2> {
_c: PhantomData,
_gc: PhantomData,
poseidon_config: self.poseidon_config.clone(),
i: Some(i),
z_0: Some(z_0),
z_i: Some(z_i),
z_i1: Some(z_i1),
phi: Some(phi1),
phiBig: Some(phi2),
phiOut: Some(phi3.clone()),
cmT: Some(cmT.0),
r: Some(r),
};
c.generate_constraints(cs.clone())?;
// get w_{i+1}
let cs1 = cs.borrow().unwrap();
let cs2 = cs1.deref();
let w_i1 = cs2.witness_assignment.clone();
let x_i1 = cs2.instance_assignment.clone();
let rW = tr1.get_challenge();
let _ = tr2.get_challenge();
// phi_{i+1} small
let phi = Phi::<C1> {
cmE: Commitment::<C1>(C1::zero()),
u: C1::ScalarField::one(),
cmW: Pedersen::commit(&self.pedersen_params_C1, &w_i1, &rW),
x: x_i1[0], // check if pos 0 is 1
};
Ok(IVCProof {
phiBig: phi3,
W: fw3.W,
phi, // phi_{i+1}
w: w_i1, // w_{i+1}
})
}
}

+ 1
- 0
src/lib.rs

@ -6,6 +6,7 @@
#![allow(dead_code)] // TMP
mod circuits;
mod ivc;
mod nifs;
mod pedersen;
mod sumcheck;

+ 4
- 4
src/nifs.rs

@ -26,10 +26,10 @@ pub struct Phi {
// FWit: Folded Witness
pub struct FWit<C: CurveGroup> {
E: Vec<C::ScalarField>,
rE: C::ScalarField,
W: Vec<C::ScalarField>,
rW: C::ScalarField,
pub E: Vec<C::ScalarField>,
pub rE: C::ScalarField,
pub W: Vec<C::ScalarField>,
pub rW: C::ScalarField,
}
impl<C: CurveGroup> FWit<C>

Loading…
Cancel
Save