/// contains [Ova](https://hackmd.io/V4838nnlRKal9ZiTHiGYzw) NIFS related circuits use ark_crypto_primitives::sponge::{constraints::AbsorbGadget, Absorb, CryptographicSponge}; use ark_ec::{CurveGroup, Group}; use ark_ff::PrimeField; use ark_r1cs_std::{ alloc::{AllocVar, AllocationMode}, boolean::Boolean, eq::EqGadget, fields::{fp::FpVar, FieldVar}, uint8::UInt8, ToConstraintFieldGadget, }; use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError}; use ark_std::fmt::Debug; use core::{borrow::Borrow, marker::PhantomData}; use super::ova::CommittedInstance; use super::NIFSGadgetTrait; use crate::folding::circuits::{nonnative::affine::NonNativeAffineVar, CF1}; use crate::folding::traits::CommittedInstanceVarOps; use crate::transcript::TranscriptVar; use crate::folding::nova::nifs::nova::ChallengeGadget; #[derive(Debug, Clone)] pub struct CommittedInstanceVar { pub u: FpVar, pub x: Vec>, pub cmWE: NonNativeAffineVar, } impl AllocVar, CF1> for CommittedInstanceVar where C: CurveGroup, { fn new_variable>>( cs: impl Into>>, f: impl FnOnce() -> Result, mode: AllocationMode, ) -> Result { f().and_then(|val| { let cs = cs.into(); let u = FpVar::::new_variable(cs.clone(), || Ok(val.borrow().u), mode)?; let x: Vec> = Vec::new_variable(cs.clone(), || Ok(val.borrow().x.clone()), mode)?; let cmWE = NonNativeAffineVar::::new_variable(cs.clone(), || Ok(val.borrow().cmWE), mode)?; Ok(Self { u, x, cmWE }) }) } } impl AbsorbGadget for CommittedInstanceVar where C: CurveGroup, ::BaseField: ark_ff::PrimeField, { fn to_sponge_bytes(&self) -> Result>, SynthesisError> { FpVar::batch_to_sponge_bytes(&self.to_sponge_field_elements()?) } fn to_sponge_field_elements(&self) -> Result>, SynthesisError> { Ok([ vec![self.u.clone()], self.x.clone(), self.cmWE.to_constraint_field()?, ] .concat()) } } impl CommittedInstanceVarOps for CommittedInstanceVar { type PointVar = NonNativeAffineVar; fn get_commitments(&self) -> Vec { vec![self.cmWE.clone()] } fn get_public_inputs(&self) -> &[FpVar>] { &self.x } fn enforce_incoming(&self) -> Result<(), SynthesisError> { self.u.enforce_equal(&FpVar::one()) } fn enforce_partial_equal(&self, other: &Self) -> Result<(), SynthesisError> { self.u.enforce_equal(&other.u)?; self.x.enforce_equal(&other.x) } } /// Implements the circuit that does the checks of the Non-Interactive Folding Scheme Verifier /// described of the Ova variant, where the cmWE check is delegated to the NIFSCycleFoldGadget. pub struct NIFSGadget, S>> { _c: PhantomData, _s: PhantomData, _t: PhantomData, } impl NIFSGadgetTrait for NIFSGadget where C: CurveGroup, S: CryptographicSponge, T: TranscriptVar, S>, ::BaseField: ark_ff::PrimeField, ::ScalarField: Absorb, ::BaseField: PrimeField, { type CommittedInstance = CommittedInstance; type CommittedInstanceVar = CommittedInstanceVar; type Proof = C::ScalarField; type ProofVar = FpVar; // unused fn verify( transcript: &mut T, pp_hash: FpVar>, U_i: Self::CommittedInstanceVar, // U_i_vec is passed to reuse the already computed U_i_vec from previous methods U_i_vec: Vec>>, u_i: Self::CommittedInstanceVar, _proof: Option, ) -> Result<(Self::CommittedInstanceVar, Vec>>), SynthesisError> { let r_bits = ChallengeGadget::>::get_challenge_gadget( transcript, pp_hash.clone(), U_i_vec, u_i.clone(), None, )?; let r = Boolean::le_bits_to_fp_var(&r_bits)?; Ok(( Self::CommittedInstanceVar { cmWE: NonNativeAffineVar::new_constant(ConstraintSystemRef::None, C::zero())?, // ci3.u = U_i.u + r * u_i.u (u_i.u is always 1 in Ova) u: U_i.u + &r, // ci3.x = U_i.x + r * u_i.x x: U_i .x .iter() .zip(u_i.x) .map(|(a, b)| a + &r * &b) .collect::>>>(), }, r_bits, )) } } #[cfg(test)] pub mod tests { use super::*; use ark_crypto_primitives::sponge::poseidon::constraints::PoseidonSpongeVar; use ark_crypto_primitives::sponge::poseidon::PoseidonSponge; use ark_pallas::{Fr, Projective}; use ark_r1cs_std::R1CSVar; use ark_std::UniformRand; use ark_std::Zero; use crate::commitment::pedersen::Pedersen; use crate::folding::nova::nifs::{ ova::NIFS, tests::{ test_committed_instance_hash_opt, test_committed_instance_to_sponge_preimage_opt, test_nifs_gadget_opt, }, }; #[test] fn test_nifs_gadget() { let mut rng = ark_std::test_rng(); // prepare the committed instances to test in-circuit let ci: Vec> = (0..2) .into_iter() .map(|_| CommittedInstance:: { u: Fr::rand(&mut rng), x: vec![Fr::rand(&mut rng); 1], cmWE: Projective::rand(&mut rng), }) .collect(); let (ci_out, ciVar_out) = test_nifs_gadget_opt::< NIFS, PoseidonSponge>, NIFSGadget, PoseidonSpongeVar>, >(ci, Fr::zero()) .unwrap(); assert_eq!(ciVar_out.u.value().unwrap(), ci_out.u); assert_eq!(ciVar_out.x.value().unwrap(), ci_out.x); } #[test] fn test_committed_instance_to_sponge_preimage() { let mut rng = ark_std::test_rng(); let ci = CommittedInstance:: { u: Fr::rand(&mut rng), x: vec![Fr::rand(&mut rng); 1], cmWE: Projective::rand(&mut rng), }; test_committed_instance_to_sponge_preimage_opt::< NIFS, PoseidonSponge>, NIFSGadget, PoseidonSpongeVar>, >(ci); } #[test] fn test_committed_instance_hash() { let mut rng = ark_std::test_rng(); let ci = CommittedInstance:: { u: Fr::rand(&mut rng), x: vec![Fr::rand(&mut rng); 1], cmWE: Projective::rand(&mut rng), }; test_committed_instance_hash_opt::< NIFS, PoseidonSponge>, NIFSGadget, PoseidonSpongeVar>, >(ci); } }