use ark_ec::CurveGroup; use ark_ff::{Field, PrimeField}; use ark_r1cs_std::{ alloc::{AllocVar, AllocationMode}, boolean::Boolean, eq::EqGadget, fields::{fp::FpVar, nonnative::NonNativeFieldVar, FieldVar}, groups::GroupOpsBounds, prelude::CurveVar, ToBitsGadget, ToConstraintFieldGadget, // groups::curves::short_weierstrass::ProjectiveVar, }; // use ark_r1cs_std::groups::curves::twisted_edwards::AffineVar; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError}; // use ark_crypto_primitives::crh::poseidon::{ // constraints::{CRHGadget, CRHParametersVar}, // CRH, // }; // use ark_crypto_primitives::crh::{CRHScheme, CRHSchemeGadget}; // use ark_crypto_primitives::snark::{FromFieldElementsGadget, SNARKGadget, SNARK}; use ark_crypto_primitives::sponge::constraints::CryptographicSpongeVar; use ark_crypto_primitives::sponge::poseidon::{ constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge, }; use core::{borrow::Borrow, marker::PhantomData}; use derivative::Derivative; use crate::nifs::Phi; pub type ConstraintF = <::BaseField as Field>::BasePrimeField; #[derive(Debug, Derivative)] #[derivative(Clone(bound = "C: CurveGroup, GC: CurveVar>"))] pub struct PhiVar>> where for<'a> &'a GC: GroupOpsBounds<'a, C, GC>, { _c: PhantomData, cmE: GC, u: NonNativeFieldVar>, cmW: GC, x: NonNativeFieldVar>, } impl AllocVar, ConstraintF> for PhiVar where C: CurveGroup, GC: CurveVar>, for<'a> &'a GC: GroupOpsBounds<'a, C, GC>, { fn new_variable>>( cs: impl Into>>, f: impl FnOnce() -> Result, mode: AllocationMode, ) -> Result { f().and_then(|val| { let cs = cs.into(); let u = NonNativeFieldVar::>::new_variable( cs.clone(), || Ok(val.borrow().u), mode, )?; let cmE = GC::new_variable(cs.clone(), || Ok(val.borrow().cmE.0), mode)?; let cmW = GC::new_variable(cs.clone(), || Ok(val.borrow().cmW.0), mode)?; let x = NonNativeFieldVar::>::new_variable( cs, || Ok(val.borrow().x), mode, )?; Ok(Self { _c: PhantomData, cmE, u, cmW, x, }) }) } } pub struct NIFSGadget>> { _c: PhantomData, _gc: PhantomData, } impl>> NIFSGadget where C: CurveGroup, GC: CurveVar>, for<'a> &'a GC: GroupOpsBounds<'a, C, GC>, { // implements the constraints for NIFS.V pub fn verify( r: NonNativeFieldVar>, cmT: GC, phi1: PhiVar, phi2: PhiVar, phi3: PhiVar, ) -> Result<(), SynthesisError> { let r2 = r.square()?; phi3.cmE.enforce_equal( &(phi1.cmE + cmT.scalar_mul_le(r.to_bits_le()?.iter())? + phi2.cmE.scalar_mul_le(r2.to_bits_le()?.iter())?), )?; phi3.u.enforce_equal(&(phi1.u + r.clone() * phi2.u))?; phi3.cmW .enforce_equal(&(phi1.cmW + phi2.cmW.scalar_mul_le(r.to_bits_le()?.iter())?))?; // wip x's check phi3.x.enforce_equal(&(phi1.x + r * phi2.x))?; Ok(()) } } use ark_crypto_primitives::sponge::Absorb; pub struct AugmentedFCircuit>> where <::BaseField as Field>::BasePrimeField: Absorb, { _c: PhantomData, _gc: PhantomData, pub poseidon_native: PoseidonSponge>, pub poseidon_config: PoseidonConfig>, pub i: Option, pub z_0: Option, pub z_i: Option, pub phi: Option>, // phi in the paper sometimes appears as phi (φ) and others as 𝗎 pub phiBig: Option>, pub phiOut: Option>, pub cmT: Option, pub r: Option, // This will not be an input and derived from a hash internally in the circuit (poseidon transcript) } impl>> ConstraintSynthesizer> for AugmentedFCircuit where C: CurveGroup, GC: CurveVar>, for<'a> &'a GC: GroupOpsBounds<'a, C, GC>, ::BaseField: PrimeField, <::BaseField as Field>::BasePrimeField: Absorb, { fn generate_constraints( self, cs: ConstraintSystemRef>, ) -> Result<(), SynthesisError> { let i = FpVar::>::new_witness(cs.clone(), || Ok(self.i.unwrap()))?; let z_0 = FpVar::>::new_witness(cs.clone(), || Ok(self.z_0.unwrap()))?; let z_i = FpVar::>::new_witness(cs.clone(), || Ok(self.z_i.unwrap()))?; let phi = PhiVar::::new_witness(cs.clone(), || Ok(self.phi.unwrap()))?; let phiBig = PhiVar::::new_witness(cs.clone(), || Ok(self.phiBig.unwrap()))?; let phiOut = PhiVar::::new_witness(cs.clone(), || Ok(self.phiOut.unwrap()))?; let cmT = GC::new_witness(cs.clone(), || Ok(self.cmT.unwrap()))?; let r = NonNativeFieldVar::>::new_witness(cs.clone(), || { Ok(self.r.unwrap()) })?; // r will come from transcript // 1. phi.x == H(vk_nifs, i, z_0, z_i, phiBig) let mut sponge = PoseidonSpongeVar::>::new(cs, &self.poseidon_config); let input = vec![i, z_0, z_i]; sponge.absorb(&input)?; let input = vec![ phiBig.u.to_constraint_field()?, phiBig.x.to_constraint_field()?, ]; sponge.absorb(&input)?; let input = vec![phiBig.cmE.to_bytes()?, phiBig.cmW.to_bytes()?]; sponge.absorb(&input)?; let h = sponge.squeeze_field_elements(1).unwrap(); let x_CF = phi.x.to_constraint_field()?; // phi.x on the ConstraintF x_CF[0].enforce_equal(&h[0])?; // review // // 2. phi.cmE==0, phi.u==1 // >>::is_zero(&phi.cmE)?; (phi.cmE.is_zero()?).enforce_equal(&Boolean::TRUE)?; (phi.u.is_one()?).enforce_equal(&Boolean::TRUE)?; // 3. nifs.verify NIFSGadget::::verify(r, cmT, phi, phiBig, phiOut)?; // 4. zksnark.V(vk_snark, phi_new, proof_phi) Ok(()) } } ////////// // pub struct Nova {} // pub trait SNARKs { // type AugmentedFunctionSNARK: SNARK; // // type FunctionSNARK: ConstraintSynthesizer; // F // type DummyStepSNARK: SNARK; // // type AugmentedFunctionCircuit: SNARKGadget; // F' // type FunctionCircuit: ConstraintSynthesizer; // F // type DummyStepCircuit: SNARKGadget; // } // pub struct TS< // MainField: PrimeField, // SecondField: PrimeField, // Config: SNARKs, // > { // augmentedF_pk: >::ProvingKey, // augmentedF_vk: >::VerifyingKey, // // dummy_pk: >::ProvingKey, // dummy_vk: >::VerifyingKey, // } #[cfg(test)] mod test { use super::*; use crate::transcript::Transcript; use ark_relations::r1cs::ConstraintSystem; use ark_std::UniformRand; use crate::nifs; use crate::pedersen; use crate::transcript::poseidon_test_config; use ark_ec::Group; // use ark_ed_on_mnt4_298::{constraints::EdwardsVar, EdwardsProjective}; use crate::pedersen::Commitment; // use ark_mnt4_298::{constraints::G1Var as MNT4G1Var, G1Projective as MNT4G1Projective} use ark_mnt4_298::{Fq, Fr}; use ark_mnt6_298::{constraints::G1Var as MNT6G1Var, G1Projective as MNT6G1Projective}; use ark_std::One; // mnt4's Fr is the Constraint Field, // while mnt4's Fq is the Field where we work, which is the C::ScalarField for C==MNT6G1 #[test] fn test_phi_var() { let phi = Phi:: { cmE: Commitment(MNT6G1Projective::generator()), u: Fq::one(), cmW: Commitment(MNT6G1Projective::generator()), x: Fq::one(), }; let cs = ConstraintSystem::::new_ref(); let _phiVar = PhiVar::::new_witness(cs.clone(), || Ok(phi)).unwrap(); // println!("num_constraints={:?}", cs.num_constraints()); } #[test] fn test_nifs_gadget() { let mut rng = ark_std::test_rng(); let pedersen_params = pedersen::Pedersen::::new_params(&mut rng, 100); // 100 is wip, will get it from actual vec let poseidon_config = poseidon_test_config::(); let cs = ConstraintSystem::::new_ref(); let (r1cs, ws, _) = nifs::gen_test_values::(2); let (A, _, _) = (r1cs.A.clone(), r1cs.B.clone(), r1cs.C.clone()); let r = Fq::rand(&mut rng); // this would come from the transcript let fw1 = nifs::FWit::::new(ws[0].clone(), A.len()); let fw2 = nifs::FWit::::new(ws[1].clone(), A.len()); let mut transcript_p = Transcript::::new(&poseidon_config); let (_fw3, phi1, phi2, _T, cmT) = nifs::NIFS::::P( &mut transcript_p, &pedersen_params, r, &r1cs, fw1, fw2, ); let phi3 = nifs::NIFS::::V(r, &phi1, &phi2, &cmT); let phi1Var = PhiVar::::new_witness(cs.clone(), || Ok(phi1)).unwrap(); let phi2Var = PhiVar::::new_witness(cs.clone(), || Ok(phi2)).unwrap(); let phi3Var = PhiVar::::new_witness(cs.clone(), || Ok(phi3)).unwrap(); let cmTVar = MNT6G1Var::new_witness(cs.clone(), || Ok(cmT.0)).unwrap(); let rVar = NonNativeFieldVar::::new_witness(cs.clone(), || Ok(r)).unwrap(); NIFSGadget::::verify(rVar, cmTVar, phi1Var, phi2Var, phi3Var) .unwrap(); // println!("num_constraints={:?}", cs.num_constraints()); } }