arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/folding-schemes/src/folding/circuits/decider/mod.rs
folding-schemes/src/folding/circuits/decider/mod.rsRaw
  1use ark_crypto_primitives::sponge::{
  2    poseidon::constraints::PoseidonSpongeVar, CryptographicSponge,
  3};
  4use ark_ec::CurveGroup;
  5use ark_ff::PrimeField;
  6use ark_poly::Polynomial;
  7use ark_r1cs_std::{
  8    fields::{fp::FpVar, FieldVar},
  9    poly::{domain::Radix2DomainVar, evaluations::univariate::EvaluationsVar},
 10    ToConstraintFieldGadget,
 11};
 12use ark_relations::r1cs::SynthesisError;
 13use ark_std::log2;
 14
 15use crate::folding::traits::{CommittedInstanceOps, CommittedInstanceVarOps, Dummy, WitnessOps};
 16use crate::transcript::{Transcript, TranscriptVar};
 17use crate::utils::vec::poly_from_vec;
 18use crate::Error;
 19use crate::{arith::Arith, folding::circuits::CF1};
 20
 21pub mod off_chain;
 22pub mod on_chain;
 23
 24/// Gadget that computes the KZG challenges.
 25/// It also offers the rust native implementation compatible with the gadget.
 26pub struct KZGChallengesGadget {}
 27
 28impl KZGChallengesGadget {
 29    pub fn get_challenges_native<
 30        C: CurveGroup,
 31        T: Transcript<CF1<C>>,
 32        U: CommittedInstanceOps<C>,
 33    >(
 34        transcript: &mut T,
 35        U_i: &U,
 36    ) -> Vec<CF1<C>> {
 37        let mut challenges = vec![];
 38        for cm in U_i.get_commitments() {
 39            transcript.absorb_nonnative(&cm);
 40            challenges.push(transcript.get_challenge());
 41        }
 42        challenges
 43    }
 44
 45    pub fn get_challenges_gadget<
 46        C: CurveGroup,
 47        S: CryptographicSponge,
 48        T: TranscriptVar<CF1<C>, S>,
 49        U: CommittedInstanceVarOps<C>,
 50    >(
 51        transcript: &mut T,
 52        U_i: &U,
 53    ) -> Result<Vec<FpVar<CF1<C>>>, SynthesisError> {
 54        let mut challenges = vec![];
 55        for cm in U_i.get_commitments() {
 56            transcript.absorb(&cm.to_constraint_field()?)?;
 57            challenges.push(transcript.get_challenge()?);
 58        }
 59        Ok(challenges)
 60    }
 61}
 62
 63/// Gadget that interpolates the polynomial from the given vector and returns
 64/// its evaluation at the given point.
 65/// It also offers the rust native implementation compatible with the gadget.
 66pub struct EvalGadget {}
 67
 68impl EvalGadget {
 69    pub fn evaluate_native<F: PrimeField>(v: &[F], point: F) -> Result<F, Error> {
 70        let mut v = v.to_vec();
 71        v.resize(v.len().next_power_of_two(), F::zero());
 72
 73        Ok(poly_from_vec(v)?.evaluate(&point))
 74    }
 75
 76    pub fn evaluate_gadget<F: PrimeField>(
 77        v: &[FpVar<F>],
 78        point: &FpVar<F>,
 79    ) -> Result<FpVar<F>, SynthesisError> {
 80        let mut v = v.to_vec();
 81        v.resize(v.len().next_power_of_two(), FpVar::zero());
 82        let n = v.len() as u64;
 83        let gen = F::get_root_of_unity(n).ok_or(SynthesisError::PolynomialDegreeTooLarge)?;
 84        // `unwrap` below is safe because `Radix2DomainVar::new` only fails if
 85        // `offset.enforce_not_equal(&FpVar::zero())` returns an error.
 86        // But in our case, `offset` is `FpVar::one()`, i.e., both operands of
 87        // `enforce_not_equal` are constants.
 88        // Consequently, `FpVar`'s implementation of `enforce_not_equal` will
 89        // always return `Ok(())`.
 90        let domain = Radix2DomainVar::new(gen, log2(v.len()) as u64, FpVar::one()).unwrap();
 91
 92        let evaluations_var = EvaluationsVar::from_vec_and_domain(v, domain, true);
 93        evaluations_var.interpolate_and_evaluate(point)
 94    }
 95}
 96
 97/// This is a temporary workaround for step 6 (running NIFS.V for group elements
 98/// in circuit) in an NIFS-agnostic way, because different folding schemes have
 99/// different interfaces of folding verification now.
100///
101/// In the future, we may introduce a better solution that uses a trait for all
102/// folding schemes that specifies their native and in-circuit behaviors.
103pub trait DeciderEnabledNIFS<
104    C: CurveGroup,
105    RU: CommittedInstanceOps<C>, // Running instance
106    IU: CommittedInstanceOps<C>, // Incoming instance
107    W: WitnessOps<CF1<C>>,
108    A: Arith<W, RU>,
109>
110{
111    type ProofDummyCfg;
112    type Proof: Dummy<Self::ProofDummyCfg>;
113    type RandomnessDummyCfg;
114    type Randomness: Dummy<Self::RandomnessDummyCfg>;
115
116    /// Fold the field elements in `U` and `u` inside the circuit.
117    ///
118    /// `U_vec` is `U` expressed as a vector of `FpVar`s, which can be reused
119    /// before or after calling this function to save constraints.
120    #[allow(clippy::too_many_arguments)]
121    fn fold_field_elements_gadget(
122        arith: &A,
123        transcript: &mut PoseidonSpongeVar<CF1<C>>,
124        pp_hash: FpVar<CF1<C>>,
125        U: RU::Var,
126        U_vec: Vec<FpVar<CF1<C>>>,
127        u: IU::Var,
128        proof: Self::Proof,
129        randomness: Self::Randomness,
130    ) -> Result<RU::Var, SynthesisError>;
131
132    /// Fold the group elements (i.e., commitments) in `U` and `u` outside the
133    /// circuit.
134    fn fold_group_elements_native(
135        U_commitments: &[C],
136        u_commitments: &[C],
137        proof: Option<Self::Proof>,
138        randomness: Self::Randomness,
139    ) -> Result<Vec<C>, Error>;
140}
141
142#[cfg(test)]
143pub mod tests {
144    use ark_crypto_primitives::sponge::{
145        constraints::CryptographicSpongeVar, poseidon::PoseidonSponge,
146    };
147    use ark_pallas::{Fr, Projective};
148    use ark_r1cs_std::{alloc::AllocVar, R1CSVar};
149    use ark_relations::r1cs::ConstraintSystem;
150    use ark_std::UniformRand;
151
152    use super::*;
153    use crate::folding::nova::{nifs::nova_circuits::CommittedInstanceVar, CommittedInstance};
154    use crate::transcript::poseidon::poseidon_canonical_config;
155
156    // checks that the gadget and native implementations of the challenge computation match
157    #[test]
158    fn test_kzg_challenge_gadget() {
159        let mut rng = ark_std::test_rng();
160        let poseidon_config = poseidon_canonical_config::<Fr>();
161        let mut transcript = PoseidonSponge::<Fr>::new(&poseidon_config);
162
163        let U_i = CommittedInstance::<Projective> {
164            cmE: Projective::rand(&mut rng),
165            u: Fr::rand(&mut rng),
166            cmW: Projective::rand(&mut rng),
167            x: vec![Fr::rand(&mut rng); 1],
168        };
169
170        // compute the challenge natively
171        let challenges = KZGChallengesGadget::get_challenges_native(&mut transcript, &U_i);
172
173        let cs = ConstraintSystem::<Fr>::new_ref();
174        let U_iVar =
175            CommittedInstanceVar::<Projective>::new_witness(cs.clone(), || Ok(U_i.clone()))
176                .unwrap();
177        let mut transcript_var = PoseidonSpongeVar::<Fr>::new(cs.clone(), &poseidon_config);
178
179        let challenges_var =
180            KZGChallengesGadget::get_challenges_gadget(&mut transcript_var, &U_iVar).unwrap();
181        assert!(cs.is_satisfied().unwrap());
182
183        // check that the natively computed and in-circuit computed hashes match
184        assert_eq!(challenges_var.value().unwrap(), challenges);
185    }
186
187    #[test]
188    fn test_polynomial_interpolation() {
189        let mut rng = ark_std::test_rng();
190        let n = 12;
191        let l = 1 << n;
192
193        let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng))
194            .take(l)
195            .collect();
196        let challenge = Fr::rand(&mut rng);
197
198        use ark_poly::Polynomial;
199        let polynomial = poly_from_vec(v.to_vec()).unwrap();
200        let eval = polynomial.evaluate(&challenge);
201
202        let cs = ConstraintSystem::<Fr>::new_ref();
203        let vVar = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(v)).unwrap();
204        let challengeVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(challenge)).unwrap();
205
206        let evalVar = EvalGadget::evaluate_gadget(&vVar, &challengeVar).unwrap();
207
208        assert_eq!(evalVar.value().unwrap(), eval);
209        assert!(cs.is_satisfied().unwrap());
210    }
211}