arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/folding-schemes/src/folding/circuits/nonnative.rs
folding-schemes/src/folding/circuits/nonnative.rsRaw
  1use ark_ec::{AffineRepr, CurveGroup};
  2use ark_r1cs_std::{
  3    alloc::{AllocVar, AllocationMode},
  4    fields::nonnative::{params::OptimizationType, AllocatedNonNativeFieldVar, NonNativeFieldVar},
  5};
  6use ark_relations::r1cs::{Namespace, SynthesisError};
  7use ark_std::{One, Zero};
  8use core::borrow::Borrow;
  9
 10/// NonNativeAffineVar represents an elliptic curve point in Affine representation in the non-native
 11/// field, over the constraint field. It is not intended to perform operations, but just to contain
 12/// the affine coordinates in order to perform hash operations of the point.
 13#[derive(Debug, Clone)]
 14pub struct NonNativeAffineVar<C: CurveGroup>
 15where
 16    <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
 17{
 18    pub x: NonNativeFieldVar<C::BaseField, C::ScalarField>,
 19    pub y: NonNativeFieldVar<C::BaseField, C::ScalarField>,
 20}
 21
 22impl<C> AllocVar<C, C::ScalarField> for NonNativeAffineVar<C>
 23where
 24    C: CurveGroup,
 25    <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
 26{
 27    fn new_variable<T: Borrow<C>>(
 28        cs: impl Into<Namespace<C::ScalarField>>,
 29        f: impl FnOnce() -> Result<T, SynthesisError>,
 30        mode: AllocationMode,
 31    ) -> Result<Self, SynthesisError> {
 32        f().and_then(|val| {
 33            let cs = cs.into();
 34
 35            let affine = val.borrow().into_affine();
 36            let zero_point = (&C::BaseField::zero(), &C::BaseField::one());
 37            let xy = affine.xy().unwrap_or(zero_point);
 38
 39            let x = NonNativeFieldVar::<C::BaseField, C::ScalarField>::new_variable(
 40                cs.clone(),
 41                || Ok(xy.0),
 42                mode,
 43            )?;
 44            let y = NonNativeFieldVar::<C::BaseField, C::ScalarField>::new_variable(
 45                cs.clone(),
 46                || Ok(xy.1),
 47                mode,
 48            )?;
 49
 50            Ok(Self { x, y })
 51        })
 52    }
 53}
 54
 55/// Wrapper on top of [`point_to_nonnative_limbs_custom_opt`] which always uses
 56/// [`OptimizationType::Weight`].
 57#[allow(clippy::type_complexity)]
 58pub fn point_to_nonnative_limbs<C: CurveGroup>(
 59    p: C,
 60) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError>
 61where
 62    <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
 63{
 64    point_to_nonnative_limbs_custom_opt(p, OptimizationType::Weight)
 65}
 66
 67/// Used to compute (outside the circuit) the limbs representation of a point that matches the one
 68/// used in-circuit, and in particular this method allows to specify which [`OptimizationType`] to
 69/// use.
 70#[allow(clippy::type_complexity)]
 71pub fn point_to_nonnative_limbs_custom_opt<C: CurveGroup>(
 72    p: C,
 73    optimization_type: OptimizationType,
 74) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError>
 75where
 76    <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
 77{
 78    let affine = p.into_affine();
 79    if affine.is_zero() {
 80        let x =
 81            AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
 82                &C::BaseField::zero(),
 83                optimization_type,
 84            )?;
 85        let y =
 86            AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
 87                &C::BaseField::one(),
 88                optimization_type,
 89            )?;
 90        return Ok((x, y));
 91    }
 92
 93    let (x, y) = affine.xy().unwrap();
 94    let x = AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
 95        x,
 96        optimization_type,
 97    )?;
 98    let y = AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
 99        y,
100        optimization_type,
101    )?;
102    Ok((x, y))
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108    use ark_pallas::{Fr, Projective};
109    use ark_r1cs_std::{alloc::AllocVar, R1CSVar, ToConstraintFieldGadget};
110    use ark_relations::r1cs::ConstraintSystem;
111    use ark_std::{UniformRand, Zero};
112
113    #[test]
114    fn test_alloc_nonnativeaffinevar() {
115        let cs = ConstraintSystem::<Fr>::new_ref();
116
117        // dealing with the 'zero' point should not panic when doing the unwrap
118        let p = Projective::zero();
119        NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap();
120
121        // check that point_to_nonnative_limbs returns the expected values
122        let mut rng = ark_std::test_rng();
123        let p = Projective::rand(&mut rng);
124        let pVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap();
125        let (x, y) = point_to_nonnative_limbs(p).unwrap();
126        assert_eq!(pVar.x.to_constraint_field().unwrap().value().unwrap(), x);
127        assert_eq!(pVar.y.to_constraint_field().unwrap().value().unwrap(), y);
128    }
129}