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