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