arnaucube
arnaucube/ark-r1cs-stdPublic Self-hosted repository
ark-r1cs-std/src/groups/curves/short_weierstrass/non_zero_affine.rs
src/groups/curves/short_weierstrass/non_zero_affine.rsRaw
  1use super::*;
  2
  3/// An affine representation of a prime order curve point that is guaranteed
  4/// to *not* be the point at infinity.
  5#[derive(Derivative)]
  6#[derivative(Debug, Clone)]
  7#[must_use]
  8pub struct NonZeroAffineVar<
  9    P: SWModelParameters,
 10    F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>,
 11> where
 12    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
 13{
 14    /// The x-coordinate.
 15    pub x: F,
 16    /// The y-coordinate.
 17    pub y: F,
 18    #[derivative(Debug = "ignore")]
 19    _params: PhantomData<P>,
 20}
 21
 22impl<P, F> NonZeroAffineVar<P, F>
 23where
 24    P: SWModelParameters,
 25    F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>,
 26    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
 27{
 28    pub(crate) fn new(x: F, y: F) -> Self {
 29        Self {
 30            x,
 31            y,
 32            _params: PhantomData,
 33        }
 34    }
 35
 36    /// Converts self into a non-zero projective point.
 37    #[tracing::instrument(target = "r1cs", skip(self))]
 38    pub(crate) fn into_projective(&self) -> ProjectiveVar<P, F> {
 39        ProjectiveVar::new(self.x.clone(), self.y.clone(), F::one())
 40    }
 41
 42    /// Performs an addition without checking that other != ±self.
 43    #[tracing::instrument(target = "r1cs", skip(self, other))]
 44    pub(crate) fn add_unchecked(&self, other: &Self) -> Result<Self, SynthesisError> {
 45        if [self, other].is_constant() {
 46            let result =
 47                (self.value()?.into_projective() + other.value()?.into_projective()).into_affine();
 48            Ok(Self::new(F::constant(result.x), F::constant(result.y)))
 49        } else {
 50            let (x1, y1) = (&self.x, &self.y);
 51            let (x2, y2) = (&other.x, &other.y);
 52            // Then,
 53            // slope lambda := (y2 - y1)/(x2 - x1);
 54            // x3 = lambda^2 - x1 - x2;
 55            // y3 = lambda * (x1 - x3) - y1
 56            let numerator = y2 - y1;
 57            let denominator = x2 - x1;
 58            let lambda = numerator.mul_by_inverse(&denominator)?;
 59            let x3 = lambda.square()? - x1 - x2;
 60            let y3 = lambda * &(x1 - &x3) - y1;
 61            Ok(Self::new(x3, y3))
 62        }
 63    }
 64
 65    /// Doubles `self`. As this is a prime order curve point,
 66    /// the output is guaranteed to not be the point at infinity.
 67    #[tracing::instrument(target = "r1cs", skip(self))]
 68    pub(crate) fn double(&self) -> Result<Self, SynthesisError> {
 69        if [self].is_constant() {
 70            let result = self.value()?.into_projective().double().into_affine();
 71            // Panic if the result is zero.
 72            assert!(!result.is_zero());
 73            Ok(Self::new(F::constant(result.x), F::constant(result.y)))
 74        } else {
 75            let (x1, y1) = (&self.x, &self.y);
 76            let x1_sqr = x1.square()?;
 77            // Then,
 78            // tangent lambda := (3 * x1^2 + a) / (2 * y1);
 79            // x3 = lambda^2 - 2x1
 80            // y3 = lambda * (x1 - x3) - y1
 81            let numerator = x1_sqr.double()? + &x1_sqr + P::COEFF_A;
 82            let denominator = y1.double()?;
 83            let lambda = numerator.mul_by_inverse(&denominator)?;
 84            let x3 = lambda.square()? - x1.double()?;
 85            let y3 = lambda * &(x1 - &x3) - y1;
 86            Ok(Self::new(x3, y3))
 87        }
 88    }
 89
 90    /// Computes `(self + other) + self`. This method requires only 5 constraints,
 91    /// less than the 7 required when computing via `self.double() + other`.
 92    ///
 93    /// This follows the formulae from [\[ELM03\]](https://arxiv.org/abs/math/0208038).
 94    #[tracing::instrument(target = "r1cs", skip(self))]
 95    pub(crate) fn double_and_add(&self, other: &Self) -> Result<Self, SynthesisError> {
 96        if [self].is_constant() || other.is_constant() {
 97            self.double()?.add_unchecked(other)
 98        } else {
 99            let (x1, y1) = (&self.x, &self.y);
100            let (x2, y2) = (&other.x, &other.y);
101
102            // Calculate self + other:
103            // slope lambda := (y2 - y1)/(x2 - x1);
104            // x3 = lambda^2 - x1 - x2;
105            // y3 = lambda * (x1 - x3) - y1
106            let numerator = y2 - y1;
107            let denominator = x2 - x1;
108            let lambda_1 = numerator.mul_by_inverse(&denominator)?;
109
110            let x3 = lambda_1.square()? - x1 - x2;
111
112            // Calculate final addition slope:
113            let lambda_2 = (lambda_1 + y1.double()?.mul_by_inverse(&(&x3 - x1))?).negate()?;
114
115            let x4 = lambda_2.square()? - x1 - x3;
116            let y4 = lambda_2 * &(x1 - &x4) - y1;
117            Ok(Self::new(x4, y4))
118        }
119    }
120
121    /// Doubles `self` in place.
122    #[tracing::instrument(target = "r1cs", skip(self))]
123    pub(crate) fn double_in_place(&mut self) -> Result<(), SynthesisError> {
124        *self = self.double()?;
125        Ok(())
126    }
127}
128
129impl<P, F> R1CSVar<<P::BaseField as Field>::BasePrimeField> for NonZeroAffineVar<P, F>
130where
131    P: SWModelParameters,
132    F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>,
133    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
134{
135    type Value = SWAffine<P>;
136
137    fn cs(&self) -> ConstraintSystemRef<<P::BaseField as Field>::BasePrimeField> {
138        self.x.cs().or(self.y.cs())
139    }
140
141    fn value(&self) -> Result<SWAffine<P>, SynthesisError> {
142        Ok(SWAffine::new(self.x.value()?, self.y.value()?, false))
143    }
144}
145
146impl<P, F> CondSelectGadget<<P::BaseField as Field>::BasePrimeField> for NonZeroAffineVar<P, F>
147where
148    P: SWModelParameters,
149    F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>,
150    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
151{
152    #[inline]
153    #[tracing::instrument(target = "r1cs")]
154    fn conditionally_select(
155        cond: &Boolean<<P::BaseField as Field>::BasePrimeField>,
156        true_value: &Self,
157        false_value: &Self,
158    ) -> Result<Self, SynthesisError> {
159        let x = cond.select(&true_value.x, &false_value.x)?;
160        let y = cond.select(&true_value.y, &false_value.y)?;
161
162        Ok(Self::new(x, y))
163    }
164}
165
166#[cfg(test)]
167mod test {
168    use crate::alloc::AllocVar;
169    use crate::fields::fp::{AllocatedFp, FpVar};
170    use crate::groups::curves::short_weierstrass::non_zero_affine::NonZeroAffineVar;
171    use crate::groups::curves::short_weierstrass::ProjectiveVar;
172    use crate::groups::CurveVar;
173    use crate::R1CSVar;
174    use ark_ec::SWModelParameters;
175    use ark_relations::r1cs::ConstraintSystem;
176    use ark_std::{vec::Vec, One};
177    use ark_test_curves::bls12_381::{g1::Parameters as G1Parameters, Fq};
178
179    #[test]
180    fn test_non_zero_affine_cost() {
181        let cs = ConstraintSystem::<Fq>::new_ref();
182
183        let x = FpVar::Var(
184            AllocatedFp::<Fq>::new_witness(cs.clone(), || {
185                Ok(G1Parameters::AFFINE_GENERATOR_COEFFS.0)
186            })
187            .unwrap(),
188        );
189        let y = FpVar::Var(
190            AllocatedFp::<Fq>::new_witness(cs.clone(), || {
191                Ok(G1Parameters::AFFINE_GENERATOR_COEFFS.1)
192            })
193            .unwrap(),
194        );
195
196        // The following code uses `double` and `add` (`add_unchecked`) to compute
197        // (1 + 2 + ... + 2^9) G
198
199        let sum_a = {
200            let mut a = ProjectiveVar::<G1Parameters, FpVar<Fq>>::new(
201                x.clone(),
202                y.clone(),
203                FpVar::Constant(Fq::one()),
204            );
205
206            let mut double_sequence = Vec::new();
207            double_sequence.push(a.clone());
208
209            for _ in 1..10 {
210                a = a.double().unwrap();
211                double_sequence.push(a.clone());
212            }
213
214            let mut sum = double_sequence[0].clone();
215            for elem in double_sequence.iter().skip(1) {
216                sum = sum + elem;
217            }
218
219            let sum = sum.value().unwrap();
220            (sum.x, sum.y)
221        };
222
223        let sum_b = {
224            let mut a = NonZeroAffineVar::<G1Parameters, FpVar<Fq>>::new(x, y);
225
226            let mut double_sequence = Vec::new();
227            double_sequence.push(a.clone());
228
229            for _ in 1..10 {
230                a = a.double().unwrap();
231                double_sequence.push(a.clone());
232            }
233
234            let mut sum = double_sequence[0].clone();
235            for elem in double_sequence.iter().skip(1) {
236                sum = sum.add_unchecked(&elem).unwrap();
237            }
238
239            (sum.x.value().unwrap(), sum.y.value().unwrap())
240        };
241
242        assert_eq!(sum_a.0, sum_b.0);
243        assert_eq!(sum_a.1, sum_b.1);
244    }
245}