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