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}