sonobe/folding-schemes/src/folding/circuits/nonnative/affine.rs
folding-schemes/src/folding/circuits/nonnative/affine.rsRaw
1use ark_ec::{AffineRepr, CurveGroup};
2use ark_r1cs_std::{
3 alloc::{AllocVar, AllocationMode},
4 fields::fp::FpVar,
5 ToConstraintFieldGadget,
6};
7use ark_relations::r1cs::{Namespace, SynthesisError};
8use ark_std::Zero;
9use core::borrow::Borrow;
10
11use crate::transcript::{AbsorbNonNative, AbsorbNonNativeGadget};
12
13use super::uint::{nonnative_field_to_field_elements, NonNativeUintVar};
14
15/// NonNativeAffineVar represents an elliptic curve point in Affine representation in the non-native
16/// field, over the constraint field. It is not intended to perform operations, but just to contain
17/// the affine coordinates in order to perform hash operations of the point.
18#[derive(Debug, Clone)]
19pub struct NonNativeAffineVar<C: CurveGroup> {
20 pub x: NonNativeUintVar<C::ScalarField>,
21 pub y: NonNativeUintVar<C::ScalarField>,
22}
23
24impl<C> AllocVar<C, C::ScalarField> for NonNativeAffineVar<C>
25where
26 C: CurveGroup,
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 = (&C::BaseField::zero(), &C::BaseField::zero());
38 let (x, y) = affine.xy().unwrap_or(zero);
39
40 let x = NonNativeUintVar::new_variable(cs.clone(), || Ok(*x), mode)?;
41 let y = NonNativeUintVar::new_variable(cs.clone(), || Ok(*y), mode)?;
42
43 Ok(Self { x, y })
44 })
45 }
46}
47
48impl<C: CurveGroup> ToConstraintFieldGadget<C::ScalarField> for NonNativeAffineVar<C> {
49 // Used for converting `NonNativeAffineVar` to a vector of `FpVar` with minimum length in
50 // the circuit.
51 fn to_constraint_field(&self) -> Result<Vec<FpVar<C::ScalarField>>, SynthesisError> {
52 let x = self.x.to_constraint_field()?;
53 let y = self.y.to_constraint_field()?;
54 Ok([x, y].concat())
55 }
56}
57
58/// The out-circuit counterpart of `NonNativeAffineVar::to_constraint_field`
59#[allow(clippy::type_complexity)]
60pub(crate) fn nonnative_affine_to_field_elements<C: CurveGroup>(
61 p: C,
62) -> (Vec<C::ScalarField>, Vec<C::ScalarField>) {
63 let affine = p.into_affine();
64 let zero = (&C::BaseField::zero(), &C::BaseField::zero());
65 let (x, y) = affine.xy().unwrap_or(zero);
66
67 let x = nonnative_field_to_field_elements(x);
68 let y = nonnative_field_to_field_elements(y);
69 (x, y)
70}
71
72impl<C: CurveGroup> NonNativeAffineVar<C> {
73 // Extracts a list of field elements of type `C::ScalarField` from the public input
74 // `p`, in exactly the same way as how `NonNativeAffineVar` is represented as limbs of type
75 // `FpVar` in-circuit.
76 #[allow(clippy::type_complexity)]
77 pub fn inputize(p: C) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError> {
78 let affine = p.into_affine();
79 let zero = (&C::BaseField::zero(), &C::BaseField::zero());
80 let (x, y) = affine.xy().unwrap_or(zero);
81
82 let x = NonNativeUintVar::inputize(*x);
83 let y = NonNativeUintVar::inputize(*y);
84 Ok((x, y))
85 }
86}
87
88impl<C: CurveGroup> AbsorbNonNative<C::ScalarField> for C {
89 fn to_native_sponge_field_elements(&self, dest: &mut Vec<C::ScalarField>) {
90 let (x, y) = nonnative_affine_to_field_elements(*self);
91 dest.extend(x);
92 dest.extend(y);
93 }
94}
95
96impl<C: CurveGroup> AbsorbNonNativeGadget<C::ScalarField> for NonNativeAffineVar<C> {
97 fn to_native_sponge_field_elements(
98 &self,
99 ) -> Result<Vec<FpVar<C::ScalarField>>, SynthesisError> {
100 self.to_constraint_field()
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use ark_pallas::{Fr, Projective};
108 use ark_r1cs_std::R1CSVar;
109 use ark_relations::r1cs::ConstraintSystem;
110 use ark_std::UniformRand;
111
112 #[test]
113 fn test_alloc_zero() {
114 let cs = ConstraintSystem::<Fr>::new_ref();
115
116 // dealing with the 'zero' point should not panic when doing the unwrap
117 let p = Projective::zero();
118 assert!(NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).is_ok());
119 }
120
121 #[test]
122 fn test_improved_to_constraint_field() {
123 let cs = ConstraintSystem::<Fr>::new_ref();
124
125 // check that point_to_nonnative_limbs returns the expected values
126 let mut rng = ark_std::test_rng();
127 let p = Projective::rand(&mut rng);
128 let pVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap();
129 let (x, y) = nonnative_affine_to_field_elements(p);
130 assert_eq!(
131 pVar.to_constraint_field().unwrap().value().unwrap(),
132 [x, y].concat()
133 );
134 }
135
136 #[test]
137 fn test_inputize() {
138 let cs = ConstraintSystem::<Fr>::new_ref();
139
140 // check that point_to_nonnative_limbs returns the expected values
141 let mut rng = ark_std::test_rng();
142 let p = Projective::rand(&mut rng);
143 let pVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap();
144 let (x, y) = NonNativeAffineVar::inputize(p).unwrap();
145
146 assert_eq!(pVar.x.0.value().unwrap(), x);
147 assert_eq!(pVar.y.0.value().unwrap(), y);
148 }
149}