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 representation 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}