sonobe/folding-schemes/src/folding/circuits/nonnative/affine.rs
folding-schemes/src/folding/circuits/nonnative/affine.rsRaw
1use ark_ec::{short_weierstrass::SWFlags, AffineRepr, CurveGroup};
2use ark_ff::{Field, PrimeField};
3use ark_r1cs_std::{
4 alloc::{AllocVar, AllocationMode},
5 fields::fp::FpVar,
6 R1CSVar, ToConstraintFieldGadget,
7};
8use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError};
9use ark_serialize::{CanonicalSerialize, CanonicalSerializeWithFlags};
10use ark_std::Zero;
11use core::borrow::Borrow;
12
13use crate::transcript::{AbsorbNonNative, AbsorbNonNativeGadget};
14
15use super::uint::{nonnative_field_to_field_elements, NonNativeUintVar};
16
17/// NonNativeAffineVar represents an elliptic curve point in Affine representation in the non-native
18/// field, over the constraint field. It is not intended to perform operations, but just to contain
19/// the affine coordinates in order to perform hash operations of the point.
20#[derive(Debug, Clone)]
21pub struct NonNativeAffineVar<C: CurveGroup> {
22 pub x: NonNativeUintVar<C::ScalarField>,
23 pub y: NonNativeUintVar<C::ScalarField>,
24}
25
26impl<C> AllocVar<C, C::ScalarField> for NonNativeAffineVar<C>
27where
28 C: CurveGroup,
29{
30 fn new_variable<T: Borrow<C>>(
31 cs: impl Into<Namespace<C::ScalarField>>,
32 f: impl FnOnce() -> Result<T, SynthesisError>,
33 mode: AllocationMode,
34 ) -> Result<Self, SynthesisError> {
35 f().and_then(|val| {
36 let cs = cs.into();
37
38 let affine = val.borrow().into_affine();
39 let zero = (&C::BaseField::zero(), &C::BaseField::zero());
40 let (x, y) = affine.xy().unwrap_or(zero);
41
42 let x = NonNativeUintVar::new_variable(cs.clone(), || Ok(*x), mode)?;
43 let y = NonNativeUintVar::new_variable(cs.clone(), || Ok(*y), mode)?;
44
45 Ok(Self { x, y })
46 })
47 }
48}
49
50impl<C: CurveGroup> R1CSVar<C::ScalarField> for NonNativeAffineVar<C> {
51 type Value = C;
52
53 fn cs(&self) -> ConstraintSystemRef<C::ScalarField> {
54 self.x.cs().or(self.y.cs())
55 }
56
57 fn value(&self) -> Result<Self::Value, SynthesisError> {
58 debug_assert_eq!(C::BaseField::extension_degree(), 1);
59
60 let x = <C::BaseField as Field>::BasePrimeField::from_le_bytes_mod_order(
61 &self.x.value()?.to_bytes_le(),
62 );
63 let y = <C::BaseField as Field>::BasePrimeField::from_le_bytes_mod_order(
64 &self.y.value()?.to_bytes_le(),
65 );
66 let mut bytes = vec![];
67 x.serialize_uncompressed(&mut bytes).unwrap();
68 y.serialize_with_flags(
69 &mut bytes,
70 if x.is_zero() && y.is_zero() {
71 SWFlags::PointAtInfinity
72 } else if y <= -y {
73 SWFlags::YIsPositive
74 } else {
75 SWFlags::YIsNegative
76 },
77 )
78 .unwrap();
79 Ok(C::deserialize_uncompressed_unchecked(&bytes[..]).unwrap())
80 }
81}
82
83impl<C: CurveGroup> ToConstraintFieldGadget<C::ScalarField> for NonNativeAffineVar<C> {
84 // Used for converting `NonNativeAffineVar` to a vector of `FpVar` with minimum length in
85 // the circuit.
86 fn to_constraint_field(&self) -> Result<Vec<FpVar<C::ScalarField>>, SynthesisError> {
87 let x = self.x.to_constraint_field()?;
88 let y = self.y.to_constraint_field()?;
89 Ok([x, y].concat())
90 }
91}
92
93/// The out-circuit counterpart of `NonNativeAffineVar::to_constraint_field`
94#[allow(clippy::type_complexity)]
95pub(crate) fn nonnative_affine_to_field_elements<C: CurveGroup>(
96 p: C,
97) -> (Vec<C::ScalarField>, Vec<C::ScalarField>) {
98 let affine = p.into_affine();
99 let zero = (&C::BaseField::zero(), &C::BaseField::zero());
100 let (x, y) = affine.xy().unwrap_or(zero);
101
102 let x = nonnative_field_to_field_elements(x);
103 let y = nonnative_field_to_field_elements(y);
104 (x, y)
105}
106
107impl<C: CurveGroup> NonNativeAffineVar<C> {
108 // Extracts a list of field elements of type `C::ScalarField` from the public input
109 // `p`, in exactly the same way as how `NonNativeAffineVar` is represented as limbs of type
110 // `FpVar` in-circuit.
111 #[allow(clippy::type_complexity)]
112 pub fn inputize(p: C) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError> {
113 let affine = p.into_affine();
114 let zero = (&C::BaseField::zero(), &C::BaseField::zero());
115 let (x, y) = affine.xy().unwrap_or(zero);
116
117 let x = NonNativeUintVar::inputize(*x);
118 let y = NonNativeUintVar::inputize(*y);
119 Ok((x, y))
120 }
121
122 pub fn zero() -> Self {
123 Self::new_constant(ConstraintSystemRef::None, C::zero()).unwrap()
124 }
125}
126
127impl<C: CurveGroup> AbsorbNonNative<C::ScalarField> for C {
128 fn to_native_sponge_field_elements(&self, dest: &mut Vec<C::ScalarField>) {
129 let (x, y) = nonnative_affine_to_field_elements(*self);
130 dest.extend(x);
131 dest.extend(y);
132 }
133}
134
135impl<C: CurveGroup> AbsorbNonNativeGadget<C::ScalarField> for NonNativeAffineVar<C> {
136 fn to_native_sponge_field_elements(
137 &self,
138 ) -> Result<Vec<FpVar<C::ScalarField>>, SynthesisError> {
139 self.to_constraint_field()
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use ark_pallas::{Fr, Projective};
147 use ark_relations::r1cs::ConstraintSystem;
148 use ark_std::UniformRand;
149
150 #[test]
151 fn test_alloc_zero() {
152 let cs = ConstraintSystem::<Fr>::new_ref();
153
154 // dealing with the 'zero' point should not panic when doing the unwrap
155 let p = Projective::zero();
156 assert!(NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).is_ok());
157 }
158
159 #[test]
160 fn test_improved_to_constraint_field() {
161 let cs = ConstraintSystem::<Fr>::new_ref();
162
163 // check that point_to_nonnative_limbs returns the expected values
164 let mut rng = ark_std::test_rng();
165 let p = Projective::rand(&mut rng);
166 let pVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap();
167 let (x, y) = nonnative_affine_to_field_elements(p);
168 assert_eq!(
169 pVar.to_constraint_field().unwrap().value().unwrap(),
170 [x, y].concat()
171 );
172 }
173
174 #[test]
175 fn test_inputize() {
176 let cs = ConstraintSystem::<Fr>::new_ref();
177
178 // check that point_to_nonnative_limbs returns the expected values
179 let mut rng = ark_std::test_rng();
180 let p = Projective::rand(&mut rng);
181 let pVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap();
182 let (x, y) = NonNativeAffineVar::inputize(p).unwrap();
183
184 assert_eq!(pVar.x.0.value().unwrap(), x);
185 assert_eq!(pVar.y.0.value().unwrap(), y);
186 }
187}