sonobe/src/folding/circuits/nonnative.rs
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 represenation 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 xy_obj = &affine.xy();
37 let mut xy = (&C::BaseField::zero(), &C::BaseField::one());
38 if xy_obj.is_some() {
39 xy = xy_obj.unwrap();
40 }
41 let x = NonNativeFieldVar::<C::BaseField, C::ScalarField>::new_variable(
42 cs.clone(),
43 || Ok(xy.0),
44 mode,
45 )?
46 .to_constraint_field()?;
47 let y = NonNativeFieldVar::<C::BaseField, C::ScalarField>::new_variable(
48 cs.clone(),
49 || Ok(xy.1),
50 mode,
51 )?
52 .to_constraint_field()?;
53
54 Ok(Self { x, y })
55 })
56 }
57}
58
59/// point_to_nonnative_limbs is used to return (outside the circuit) the limbs representation that
60/// matches the one used in-circuit.
61#[allow(clippy::type_complexity)]
62pub fn point_to_nonnative_limbs<C: CurveGroup>(
63 p: C,
64) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError>
65where
66 <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
67{
68 let affine = p.into_affine();
69 if affine.is_zero() {
70 let x =
71 AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
72 &C::BaseField::zero(),
73 OptimizationType::Weight,
74 )?;
75 let y =
76 AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
77 &C::BaseField::one(),
78 OptimizationType::Weight,
79 )?;
80 return Ok((x, y));
81 }
82
83 let (x, y) = affine.xy().unwrap();
84 let x = AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
85 x,
86 OptimizationType::Weight,
87 )?;
88 let y = AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
89 y,
90 OptimizationType::Weight,
91 )?;
92 Ok((x, y))
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use ark_pallas::{Fr, Projective};
99 use ark_r1cs_std::alloc::AllocVar;
100 use ark_relations::r1cs::ConstraintSystem;
101 use ark_std::Zero;
102
103 #[test]
104 fn test_alloc_nonnativeaffinevar_zero() {
105 let cs = ConstraintSystem::<Fr>::new_ref();
106
107 // dealing with the 'zero' point should not panic when doing the unwrap
108 let p = Projective::zero();
109 NonNativeAffineVar::<Fr>::new_witness(cs.clone(), || Ok(p)).unwrap();
110 }
111}