sonobe/folding-schemes/src/folding/nova/nifs/ova_circuits.rs
folding-schemes/src/folding/nova/nifs/ova_circuits.rsRaw
1/// contains [Ova](https://hackmd.io/V4838nnlRKal9ZiTHiGYzw) NIFS related circuits
2use ark_crypto_primitives::sponge::{constraints::AbsorbGadget, Absorb, CryptographicSponge};
3use ark_ec::{CurveGroup, Group};
4use ark_ff::PrimeField;
5use ark_r1cs_std::{
6 alloc::{AllocVar, AllocationMode},
7 boolean::Boolean,
8 eq::EqGadget,
9 fields::{fp::FpVar, FieldVar},
10 uint8::UInt8,
11 ToConstraintFieldGadget,
12};
13use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError};
14use ark_std::fmt::Debug;
15use core::{borrow::Borrow, marker::PhantomData};
16
17use super::ova::CommittedInstance;
18use super::NIFSGadgetTrait;
19use crate::folding::circuits::{nonnative::affine::NonNativeAffineVar, CF1};
20use crate::folding::traits::CommittedInstanceVarOps;
21use crate::transcript::TranscriptVar;
22
23use crate::folding::nova::nifs::nova::ChallengeGadget;
24
25#[derive(Debug, Clone)]
26pub struct CommittedInstanceVar<C: CurveGroup> {
27 pub u: FpVar<C::ScalarField>,
28 pub x: Vec<FpVar<C::ScalarField>>,
29 pub cmWE: NonNativeAffineVar<C>,
30}
31
32impl<C> AllocVar<CommittedInstance<C>, CF1<C>> for CommittedInstanceVar<C>
33where
34 C: CurveGroup,
35{
36 fn new_variable<T: Borrow<CommittedInstance<C>>>(
37 cs: impl Into<Namespace<CF1<C>>>,
38 f: impl FnOnce() -> Result<T, SynthesisError>,
39 mode: AllocationMode,
40 ) -> Result<Self, SynthesisError> {
41 f().and_then(|val| {
42 let cs = cs.into();
43
44 let u = FpVar::<C::ScalarField>::new_variable(cs.clone(), || Ok(val.borrow().u), mode)?;
45 let x: Vec<FpVar<C::ScalarField>> =
46 Vec::new_variable(cs.clone(), || Ok(val.borrow().x.clone()), mode)?;
47
48 let cmWE =
49 NonNativeAffineVar::<C>::new_variable(cs.clone(), || Ok(val.borrow().cmWE), mode)?;
50
51 Ok(Self { u, x, cmWE })
52 })
53 }
54}
55
56impl<C> AbsorbGadget<C::ScalarField> for CommittedInstanceVar<C>
57where
58 C: CurveGroup,
59 <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
60{
61 fn to_sponge_bytes(&self) -> Result<Vec<UInt8<C::ScalarField>>, SynthesisError> {
62 FpVar::batch_to_sponge_bytes(&self.to_sponge_field_elements()?)
63 }
64
65 fn to_sponge_field_elements(&self) -> Result<Vec<FpVar<C::ScalarField>>, SynthesisError> {
66 Ok([
67 vec![self.u.clone()],
68 self.x.clone(),
69 self.cmWE.to_constraint_field()?,
70 ]
71 .concat())
72 }
73}
74
75impl<C: CurveGroup> CommittedInstanceVarOps<C> for CommittedInstanceVar<C> {
76 type PointVar = NonNativeAffineVar<C>;
77
78 fn get_commitments(&self) -> Vec<Self::PointVar> {
79 vec![self.cmWE.clone()]
80 }
81
82 fn get_public_inputs(&self) -> &[FpVar<CF1<C>>] {
83 &self.x
84 }
85
86 fn enforce_incoming(&self) -> Result<(), SynthesisError> {
87 self.u.enforce_equal(&FpVar::one())
88 }
89
90 fn enforce_partial_equal(&self, other: &Self) -> Result<(), SynthesisError> {
91 self.u.enforce_equal(&other.u)?;
92 self.x.enforce_equal(&other.x)
93 }
94}
95
96/// Implements the circuit that does the checks of the Non-Interactive Folding Scheme Verifier
97/// described of the Ova variant, where the cmWE check is delegated to the NIFSCycleFoldGadget.
98pub struct NIFSGadget<C: CurveGroup, S: CryptographicSponge, T: TranscriptVar<CF1<C>, S>> {
99 _c: PhantomData<C>,
100 _s: PhantomData<S>,
101 _t: PhantomData<T>,
102}
103
104impl<C, S, T> NIFSGadgetTrait<C, S, T> for NIFSGadget<C, S, T>
105where
106 C: CurveGroup,
107 S: CryptographicSponge,
108 T: TranscriptVar<CF1<C>, S>,
109 <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
110
111 <C as Group>::ScalarField: Absorb,
112 <C as CurveGroup>::BaseField: PrimeField,
113{
114 type CommittedInstance = CommittedInstance<C>;
115 type CommittedInstanceVar = CommittedInstanceVar<C>;
116 type Proof = C::ScalarField;
117 type ProofVar = FpVar<C::ScalarField>; // unused
118
119 fn verify(
120 transcript: &mut T,
121 pp_hash: FpVar<CF1<C>>,
122 U_i: Self::CommittedInstanceVar,
123 // U_i_vec is passed to reuse the already computed U_i_vec from previous methods
124 U_i_vec: Vec<FpVar<CF1<C>>>,
125 u_i: Self::CommittedInstanceVar,
126 _proof: Option<Self::ProofVar>,
127 ) -> Result<(Self::CommittedInstanceVar, Vec<Boolean<CF1<C>>>), SynthesisError> {
128 let r_bits = ChallengeGadget::<C, CommittedInstance<C>>::get_challenge_gadget(
129 transcript,
130 pp_hash.clone(),
131 U_i_vec,
132 u_i.clone(),
133 None,
134 )?;
135 let r = Boolean::le_bits_to_fp_var(&r_bits)?;
136
137 Ok((
138 Self::CommittedInstanceVar {
139 cmWE: NonNativeAffineVar::new_constant(ConstraintSystemRef::None, C::zero())?,
140 // ci3.u = U_i.u + r * u_i.u (u_i.u is always 1 in Ova)
141 u: U_i.u + &r,
142 // ci3.x = U_i.x + r * u_i.x
143 x: U_i
144 .x
145 .iter()
146 .zip(u_i.x)
147 .map(|(a, b)| a + &r * &b)
148 .collect::<Vec<FpVar<CF1<C>>>>(),
149 },
150 r_bits,
151 ))
152 }
153}
154
155#[cfg(test)]
156pub mod tests {
157 use super::*;
158 use ark_crypto_primitives::sponge::poseidon::constraints::PoseidonSpongeVar;
159 use ark_crypto_primitives::sponge::poseidon::PoseidonSponge;
160 use ark_pallas::{Fr, Projective};
161 use ark_r1cs_std::R1CSVar;
162 use ark_std::UniformRand;
163 use ark_std::Zero;
164
165 use crate::commitment::pedersen::Pedersen;
166 use crate::folding::nova::nifs::{
167 ova::NIFS,
168 tests::{
169 test_committed_instance_hash_opt, test_committed_instance_to_sponge_preimage_opt,
170 test_nifs_gadget_opt,
171 },
172 };
173
174 #[test]
175 fn test_nifs_gadget() {
176 let mut rng = ark_std::test_rng();
177 // prepare the committed instances to test in-circuit
178 let ci: Vec<CommittedInstance<Projective>> = (0..2)
179 .into_iter()
180 .map(|_| CommittedInstance::<Projective> {
181 u: Fr::rand(&mut rng),
182 x: vec![Fr::rand(&mut rng); 1],
183 cmWE: Projective::rand(&mut rng),
184 })
185 .collect();
186
187 let (ci_out, ciVar_out) = test_nifs_gadget_opt::<
188 NIFS<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>,
189 NIFSGadget<Projective, PoseidonSponge<Fr>, PoseidonSpongeVar<Fr>>,
190 >(ci, Fr::zero())
191 .unwrap();
192 assert_eq!(ciVar_out.u.value().unwrap(), ci_out.u);
193 assert_eq!(ciVar_out.x.value().unwrap(), ci_out.x);
194 }
195
196 #[test]
197 fn test_committed_instance_to_sponge_preimage() {
198 let mut rng = ark_std::test_rng();
199 let ci = CommittedInstance::<Projective> {
200 u: Fr::rand(&mut rng),
201 x: vec![Fr::rand(&mut rng); 1],
202 cmWE: Projective::rand(&mut rng),
203 };
204
205 test_committed_instance_to_sponge_preimage_opt::<
206 NIFS<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>,
207 NIFSGadget<Projective, PoseidonSponge<Fr>, PoseidonSpongeVar<Fr>>,
208 >(ci);
209 }
210
211 #[test]
212 fn test_committed_instance_hash() {
213 let mut rng = ark_std::test_rng();
214 let ci = CommittedInstance::<Projective> {
215 u: Fr::rand(&mut rng),
216 x: vec![Fr::rand(&mut rng); 1],
217 cmWE: Projective::rand(&mut rng),
218 };
219 test_committed_instance_hash_opt::<
220 NIFS<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>,
221 NIFSGadget<Projective, PoseidonSponge<Fr>, PoseidonSpongeVar<Fr>>,
222 >(ci);
223 }
224}