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::{BigInteger, PrimeField};
  3use ark_r1cs_std::{
  4    alloc::{AllocVar, AllocationMode},
  5    fields::{
  6        fp::FpVar,
  7        nonnative::{params::OptimizationType, AllocatedNonNativeFieldVar, NonNativeFieldVar},
  8        FieldVar,
  9    },
 10    ToBitsGadget,
 11};
 12use ark_relations::r1cs::{Namespace, SynthesisError};
 13use ark_std::Zero;
 14use core::borrow::Borrow;
 15
 16/// A more efficient version of `NonNativeFieldVar::to_constraint_field`
 17pub fn nonnative_field_var_to_constraint_field<TargetField: PrimeField, BaseField: PrimeField>(
 18    f: &NonNativeFieldVar<TargetField, BaseField>,
 19) -> Result<Vec<FpVar<BaseField>>, SynthesisError> {
 20    let bits = f.to_bits_le()?;
 21
 22    let bits_per_limb = BaseField::MODULUS_BIT_SIZE as usize - 1;
 23    let num_limbs = (TargetField::MODULUS_BIT_SIZE as usize).div_ceil(bits_per_limb);
 24
 25    let mut limbs = bits
 26        .chunks(bits_per_limb)
 27        .map(|chunk| {
 28            let mut limb = FpVar::<BaseField>::zero();
 29            let mut w = BaseField::one();
 30            for b in chunk.iter() {
 31                limb += FpVar::from(b.clone()) * w;
 32                w.double_in_place();
 33            }
 34            limb
 35        })
 36        .collect::<Vec<FpVar<BaseField>>>();
 37    limbs.resize(num_limbs, FpVar::zero());
 38    limbs.reverse();
 39
 40    Ok(limbs)
 41}
 42
 43/// The out-circuit counterpart of `nonnative_field_var_to_constraint_field`
 44pub fn nonnative_field_to_field_elements<TargetField: PrimeField, BaseField: PrimeField>(
 45    f: &TargetField,
 46) -> Vec<BaseField> {
 47    let bits = f.into_bigint().to_bits_le();
 48
 49    let bits_per_limb = BaseField::MODULUS_BIT_SIZE as usize - 1;
 50    let num_limbs = (TargetField::MODULUS_BIT_SIZE as usize).div_ceil(bits_per_limb);
 51
 52    let mut limbs = bits
 53        .chunks(bits_per_limb)
 54        .map(|chunk| {
 55            let mut limb = BaseField::zero();
 56            let mut w = BaseField::one();
 57            for &b in chunk.iter() {
 58                limb += BaseField::from(b) * w;
 59                w.double_in_place();
 60            }
 61            limb
 62        })
 63        .collect::<Vec<BaseField>>();
 64    limbs.resize(num_limbs, BaseField::zero());
 65    limbs.reverse();
 66
 67    limbs
 68}
 69
 70/// NonNativeAffineVar represents an elliptic curve point in Affine representation in the non-native
 71/// field, over the constraint field. It is not intended to perform operations, but just to contain
 72/// the affine coordinates in order to perform hash operations of the point.
 73#[derive(Debug, Clone)]
 74pub struct NonNativeAffineVar<C: CurveGroup>
 75where
 76    <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
 77{
 78    pub x: NonNativeFieldVar<C::BaseField, C::ScalarField>,
 79    pub y: NonNativeFieldVar<C::BaseField, C::ScalarField>,
 80}
 81
 82impl<C> AllocVar<C, C::ScalarField> for NonNativeAffineVar<C>
 83where
 84    C: CurveGroup,
 85    <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
 86{
 87    fn new_variable<T: Borrow<C>>(
 88        cs: impl Into<Namespace<C::ScalarField>>,
 89        f: impl FnOnce() -> Result<T, SynthesisError>,
 90        mode: AllocationMode,
 91    ) -> Result<Self, SynthesisError> {
 92        f().and_then(|val| {
 93            let cs = cs.into();
 94
 95            let affine = val.borrow().into_affine();
 96            let zero_point = (&C::BaseField::zero(), &C::BaseField::zero());
 97            let xy = affine.xy().unwrap_or(zero_point);
 98
 99            let x = NonNativeFieldVar::<C::BaseField, C::ScalarField>::new_variable(
100                cs.clone(),
101                || Ok(xy.0),
102                mode,
103            )?;
104            let y = NonNativeFieldVar::<C::BaseField, C::ScalarField>::new_variable(
105                cs.clone(),
106                || Ok(xy.1),
107                mode,
108            )?;
109
110            Ok(Self { x, y })
111        })
112    }
113}
114
115/// Wrapper on top of [`point_to_nonnative_limbs_custom_opt`] which always uses
116/// [`OptimizationType::Weight`].
117#[allow(clippy::type_complexity)]
118pub fn point_to_nonnative_limbs<C: CurveGroup>(
119    p: C,
120) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError>
121where
122    <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
123{
124    point_to_nonnative_limbs_custom_opt(p, OptimizationType::Weight)
125}
126
127/// Used to compute (outside the circuit) the limbs representation of a point that matches the one
128/// used in-circuit, and in particular this method allows to specify which [`OptimizationType`] to
129/// use.
130#[allow(clippy::type_complexity)]
131pub fn point_to_nonnative_limbs_custom_opt<C: CurveGroup>(
132    p: C,
133    optimization_type: OptimizationType,
134) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError>
135where
136    <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
137{
138    let affine = p.into_affine();
139    if affine.is_zero() {
140        let x =
141            AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
142                &C::BaseField::zero(),
143                optimization_type,
144            )?;
145        let y =
146            AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
147                &C::BaseField::zero(),
148                optimization_type,
149            )?;
150        return Ok((x, y));
151    }
152
153    let (x, y) = affine.xy().unwrap();
154    let x = AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
155        x,
156        optimization_type,
157    )?;
158    let y = AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
159        y,
160        optimization_type,
161    )?;
162    Ok((x, y))
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168    use ark_pallas::{Fr, Projective};
169    use ark_r1cs_std::{alloc::AllocVar, R1CSVar, ToConstraintFieldGadget};
170    use ark_relations::r1cs::ConstraintSystem;
171    use ark_std::{UniformRand, Zero};
172
173    #[test]
174    fn test_alloc_nonnativeaffinevar() {
175        let cs = ConstraintSystem::<Fr>::new_ref();
176
177        // dealing with the 'zero' point should not panic when doing the unwrap
178        let p = Projective::zero();
179        NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap();
180
181        // check that point_to_nonnative_limbs returns the expected values
182        let mut rng = ark_std::test_rng();
183        let p = Projective::rand(&mut rng);
184        let pVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap();
185        let (x, y) = point_to_nonnative_limbs(p).unwrap();
186        assert_eq!(pVar.x.to_constraint_field().unwrap().value().unwrap(), x);
187        assert_eq!(pVar.y.to_constraint_field().unwrap().value().unwrap(), y);
188    }
189}