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