arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/folding-schemes/src/folding/circuits/nonnative/affine.rs
folding-schemes/src/folding/circuits/nonnative/affine.rsRaw
  1use ark_ec::{short_weierstrass::SWFlags, AffineRepr, CurveGroup};
  2use ark_ff::{Field, PrimeField};
  3use ark_r1cs_std::{
  4    alloc::{AllocVar, AllocationMode},
  5    fields::fp::FpVar,
  6    R1CSVar, ToConstraintFieldGadget,
  7};
  8use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError};
  9use ark_serialize::{CanonicalSerialize, CanonicalSerializeWithFlags};
 10use ark_std::Zero;
 11use core::borrow::Borrow;
 12
 13use crate::transcript::{AbsorbNonNative, AbsorbNonNativeGadget};
 14
 15use super::uint::{nonnative_field_to_field_elements, NonNativeUintVar};
 16
 17/// NonNativeAffineVar represents an elliptic curve point in Affine representation in the non-native
 18/// field, over the constraint field. It is not intended to perform operations, but just to contain
 19/// the affine coordinates in order to perform hash operations of the point.
 20#[derive(Debug, Clone)]
 21pub struct NonNativeAffineVar<C: CurveGroup> {
 22    pub x: NonNativeUintVar<C::ScalarField>,
 23    pub y: NonNativeUintVar<C::ScalarField>,
 24}
 25
 26impl<C> AllocVar<C, C::ScalarField> for NonNativeAffineVar<C>
 27where
 28    C: CurveGroup,
 29{
 30    fn new_variable<T: Borrow<C>>(
 31        cs: impl Into<Namespace<C::ScalarField>>,
 32        f: impl FnOnce() -> Result<T, SynthesisError>,
 33        mode: AllocationMode,
 34    ) -> Result<Self, SynthesisError> {
 35        f().and_then(|val| {
 36            let cs = cs.into();
 37
 38            let affine = val.borrow().into_affine();
 39            let zero = (&C::BaseField::zero(), &C::BaseField::zero());
 40            let (x, y) = affine.xy().unwrap_or(zero);
 41
 42            let x = NonNativeUintVar::new_variable(cs.clone(), || Ok(*x), mode)?;
 43            let y = NonNativeUintVar::new_variable(cs.clone(), || Ok(*y), mode)?;
 44
 45            Ok(Self { x, y })
 46        })
 47    }
 48}
 49
 50impl<C: CurveGroup> R1CSVar<C::ScalarField> for NonNativeAffineVar<C> {
 51    type Value = C;
 52
 53    fn cs(&self) -> ConstraintSystemRef<C::ScalarField> {
 54        self.x.cs().or(self.y.cs())
 55    }
 56
 57    fn value(&self) -> Result<Self::Value, SynthesisError> {
 58        debug_assert_eq!(C::BaseField::extension_degree(), 1);
 59
 60        let x = <C::BaseField as Field>::BasePrimeField::from_le_bytes_mod_order(
 61            &self.x.value()?.to_bytes_le(),
 62        );
 63        let y = <C::BaseField as Field>::BasePrimeField::from_le_bytes_mod_order(
 64            &self.y.value()?.to_bytes_le(),
 65        );
 66        let mut bytes = vec![];
 67        x.serialize_uncompressed(&mut bytes).unwrap();
 68        y.serialize_with_flags(
 69            &mut bytes,
 70            if x.is_zero() && y.is_zero() {
 71                SWFlags::PointAtInfinity
 72            } else if y <= -y {
 73                SWFlags::YIsPositive
 74            } else {
 75                SWFlags::YIsNegative
 76            },
 77        )
 78        .unwrap();
 79        Ok(C::deserialize_uncompressed_unchecked(&bytes[..]).unwrap())
 80    }
 81}
 82
 83impl<C: CurveGroup> ToConstraintFieldGadget<C::ScalarField> for NonNativeAffineVar<C> {
 84    // Used for converting `NonNativeAffineVar` to a vector of `FpVar` with minimum length in
 85    // the circuit.
 86    fn to_constraint_field(&self) -> Result<Vec<FpVar<C::ScalarField>>, SynthesisError> {
 87        let x = self.x.to_constraint_field()?;
 88        let y = self.y.to_constraint_field()?;
 89        Ok([x, y].concat())
 90    }
 91}
 92
 93/// The out-circuit counterpart of `NonNativeAffineVar::to_constraint_field`
 94#[allow(clippy::type_complexity)]
 95pub(crate) fn nonnative_affine_to_field_elements<C: CurveGroup>(
 96    p: C,
 97) -> (Vec<C::ScalarField>, Vec<C::ScalarField>) {
 98    let affine = p.into_affine();
 99    let zero = (&C::BaseField::zero(), &C::BaseField::zero());
100    let (x, y) = affine.xy().unwrap_or(zero);
101
102    let x = nonnative_field_to_field_elements(x);
103    let y = nonnative_field_to_field_elements(y);
104    (x, y)
105}
106
107impl<C: CurveGroup> NonNativeAffineVar<C> {
108    // Extracts a list of field elements of type `C::ScalarField` from the public input
109    // `p`, in exactly the same way as how `NonNativeAffineVar` is represented as limbs of type
110    // `FpVar` in-circuit.
111    #[allow(clippy::type_complexity)]
112    pub fn inputize(p: C) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError> {
113        let affine = p.into_affine();
114        let zero = (&C::BaseField::zero(), &C::BaseField::zero());
115        let (x, y) = affine.xy().unwrap_or(zero);
116
117        let x = NonNativeUintVar::inputize(*x);
118        let y = NonNativeUintVar::inputize(*y);
119        Ok((x, y))
120    }
121
122    pub fn zero() -> Self {
123        Self::new_constant(ConstraintSystemRef::None, C::zero()).unwrap()
124    }
125}
126
127impl<C: CurveGroup> AbsorbNonNative<C::ScalarField> for C {
128    fn to_native_sponge_field_elements(&self, dest: &mut Vec<C::ScalarField>) {
129        let (x, y) = nonnative_affine_to_field_elements(*self);
130        dest.extend(x);
131        dest.extend(y);
132    }
133}
134
135impl<C: CurveGroup> AbsorbNonNativeGadget<C::ScalarField> for NonNativeAffineVar<C> {
136    fn to_native_sponge_field_elements(
137        &self,
138    ) -> Result<Vec<FpVar<C::ScalarField>>, SynthesisError> {
139        self.to_constraint_field()
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146    use ark_pallas::{Fr, Projective};
147    use ark_relations::r1cs::ConstraintSystem;
148    use ark_std::UniformRand;
149
150    #[test]
151    fn test_alloc_zero() {
152        let cs = ConstraintSystem::<Fr>::new_ref();
153
154        // dealing with the 'zero' point should not panic when doing the unwrap
155        let p = Projective::zero();
156        assert!(NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).is_ok());
157    }
158
159    #[test]
160    fn test_improved_to_constraint_field() {
161        let cs = ConstraintSystem::<Fr>::new_ref();
162
163        // check that point_to_nonnative_limbs returns the expected values
164        let mut rng = ark_std::test_rng();
165        let p = Projective::rand(&mut rng);
166        let pVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap();
167        let (x, y) = nonnative_affine_to_field_elements(p);
168        assert_eq!(
169            pVar.to_constraint_field().unwrap().value().unwrap(),
170            [x, y].concat()
171        );
172    }
173
174    #[test]
175    fn test_inputize() {
176        let cs = ConstraintSystem::<Fr>::new_ref();
177
178        // check that point_to_nonnative_limbs returns the expected values
179        let mut rng = ark_std::test_rng();
180        let p = Projective::rand(&mut rng);
181        let pVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap();
182        let (x, y) = NonNativeAffineVar::inputize(p).unwrap();
183
184        assert_eq!(pVar.x.0.value().unwrap(), x);
185        assert_eq!(pVar.y.0.value().unwrap(), y);
186    }
187}