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_ff::PrimeField;
  3use ark_r1cs_std::fields::nonnative::{params::OptimizationType, AllocatedNonNativeFieldVar};
  4use ark_r1cs_std::{
  5    alloc::{AllocVar, AllocationMode},
  6    fields::{fp::FpVar, nonnative::NonNativeFieldVar},
  7    ToConstraintFieldGadget,
  8};
  9use ark_relations::r1cs::{Namespace, SynthesisError};
 10use ark_std::{One, Zero};
 11use core::borrow::Borrow;
 12
 13/// NonNativeAffineVar represents an elliptic curve point in Affine represenation 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<F: PrimeField> {
 18    pub x: Vec<FpVar<F>>,
 19    pub y: Vec<FpVar<F>>,
 20}
 21
 22impl<C> AllocVar<C, C::ScalarField> for NonNativeAffineVar<C::ScalarField>
 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            .to_constraint_field()?;
 45            let y = NonNativeFieldVar::<C::BaseField, C::ScalarField>::new_variable(
 46                cs.clone(),
 47                || Ok(xy.1),
 48                mode,
 49            )?
 50            .to_constraint_field()?;
 51
 52            Ok(Self { x, y })
 53        })
 54    }
 55}
 56
 57/// Wrapper on top of [`point_to_nonnative_limbs_custom_opt`] which always uses
 58/// [`OptimizationType::Weight`].
 59#[allow(clippy::type_complexity)]
 60pub fn point_to_nonnative_limbs<C: CurveGroup>(
 61    p: C,
 62) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError>
 63where
 64    <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
 65{
 66    point_to_nonnative_limbs_custom_opt(p, OptimizationType::Weight)
 67}
 68
 69/// Used to compute (outside the circuit) the limbs representation of a point that matches the one
 70/// used in-circuit, and in particular this method allows to specify which [`OptimizationType`] to
 71/// use.
 72#[allow(clippy::type_complexity)]
 73pub fn point_to_nonnative_limbs_custom_opt<C: CurveGroup>(
 74    p: C,
 75    optimization_type: OptimizationType,
 76) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError>
 77where
 78    <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
 79{
 80    let affine = p.into_affine();
 81    if affine.is_zero() {
 82        let x =
 83            AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
 84                &C::BaseField::zero(),
 85                optimization_type,
 86            )?;
 87        let y =
 88            AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
 89                &C::BaseField::one(),
 90                optimization_type,
 91            )?;
 92        return Ok((x, y));
 93    }
 94
 95    let (x, y) = affine.xy().unwrap();
 96    let x = AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
 97        x,
 98        optimization_type,
 99    )?;
100    let y = AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
101        y,
102        optimization_type,
103    )?;
104    Ok((x, y))
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110    use ark_pallas::{Fr, Projective};
111    use ark_r1cs_std::{alloc::AllocVar, R1CSVar};
112    use ark_relations::r1cs::ConstraintSystem;
113    use ark_std::{UniformRand, Zero};
114
115    #[test]
116    fn test_alloc_nonnativeaffinevar() {
117        let cs = ConstraintSystem::<Fr>::new_ref();
118
119        // dealing with the 'zero' point should not panic when doing the unwrap
120        let p = Projective::zero();
121        NonNativeAffineVar::<Fr>::new_witness(cs.clone(), || Ok(p)).unwrap();
122
123        // check that point_to_nonnative_limbs returns the expected values
124        let mut rng = ark_std::test_rng();
125        let p = Projective::rand(&mut rng);
126        let pVar = NonNativeAffineVar::<Fr>::new_witness(cs.clone(), || Ok(p)).unwrap();
127        let (x, y) = point_to_nonnative_limbs(p).unwrap();
128        assert_eq!(pVar.x.value().unwrap(), x);
129        assert_eq!(pVar.y.value().unwrap(), y);
130    }
131}