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 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/// point_to_nonnative_limbs is used to compute (outside the circuit) the limbs representation of a
58/// point that matches the one used in-circuit.
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 let affine = p.into_affine();
67 if affine.is_zero() {
68 let x =
69 AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
70 &C::BaseField::zero(),
71 OptimizationType::Weight,
72 )?;
73 let y =
74 AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
75 &C::BaseField::one(),
76 OptimizationType::Weight,
77 )?;
78 return Ok((x, y));
79 }
80
81 let (x, y) = affine.xy().unwrap();
82 let x = AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
83 x,
84 OptimizationType::Weight,
85 )?;
86 let y = AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
87 y,
88 OptimizationType::Weight,
89 )?;
90 Ok((x, y))
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use ark_pallas::{Fr, Projective};
97 use ark_r1cs_std::alloc::AllocVar;
98 use ark_relations::r1cs::ConstraintSystem;
99 use ark_std::Zero;
100
101 #[test]
102 fn test_alloc_nonnativeaffinevar_zero() {
103 let cs = ConstraintSystem::<Fr>::new_ref();
104
105 // dealing with the 'zero' point should not panic when doing the unwrap
106 let p = Projective::zero();
107 NonNativeAffineVar::<Fr>::new_witness(cs.clone(), || Ok(p)).unwrap();
108 }
109}