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/// An affine representation of a prime order curve point that is guaranteed
  3/// to *not* be the point at infinity.
  4#[derive(Derivative)]
  5#[derivative(Debug, Clone)]
  6#[must_use]
  7pub struct NonZeroAffineVar<
  8    P: SWModelParameters,
  9    F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>,
 10> where
 11    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
 12{
 13    /// The x-coordinate.
 14    pub x: F,
 15    /// The y-coordinate.
 16    pub y: F,
 17    #[derivative(Debug = "ignore")]
 18    _params: PhantomData<P>,
 19}
 20
 21impl<P, F> NonZeroAffineVar<P, F>
 22where
 23    P: SWModelParameters,
 24    F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>,
 25    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
 26{
 27    pub(crate) fn new(x: F, y: F) -> Self {
 28        Self {
 29            x,
 30            y,
 31            _params: PhantomData,
 32        }
 33    }
 34
 35    /// Converts self into a non-zero projective point.
 36    #[tracing::instrument(target = "r1cs", skip(self))]
 37    pub(crate) fn into_projective(&self) -> ProjectiveVar<P, F> {
 38        ProjectiveVar::new(self.x.clone(), self.y.clone(), F::one())
 39    }
 40
 41    /// Performs an addition without checking that other != ±self.
 42    #[tracing::instrument(target = "r1cs", skip(self, other))]
 43    pub(crate) fn add_unchecked(&self, other: &Self) -> Result<Self, SynthesisError> {
 44        if [self, other].is_constant() {
 45            let result =
 46                (self.value()?.into_projective() + other.value()?.into_projective()).into_affine();
 47            Ok(Self::new(F::constant(result.x), F::constant(result.y)))
 48        } else {
 49            let (x1, y1) = (&self.x, &self.y);
 50            let (x2, y2) = (&other.x, &other.y);
 51            // Then,
 52            // slope lambda := (y2 - y1)/(x2 - x1);
 53            // x3 = lambda^2 - x1 - x2;
 54            // y3 = lambda * (x1 - x3) - y1
 55            let numerator = y2 - y1;
 56            let denominator = x2 - x1;
 57            let lambda = numerator.mul_by_inverse(&denominator)?;
 58            let x3 = lambda.square()? - x1 - x2;
 59            let y3 = lambda * &(x1 - &x3) - y1;
 60            Ok(Self::new(x3, y3))
 61        }
 62    }
 63
 64    /// Doubles `self`. As this is a prime order curve point,
 65    /// the output is guaranteed to not be the point at infinity.
 66    #[tracing::instrument(target = "r1cs", skip(self))]
 67    pub(crate) fn double(&self) -> Result<Self, SynthesisError> {
 68        if [self].is_constant() {
 69            let result = self.value()?.into_projective().double().into_affine();
 70            // Panic if the result is zero.
 71            assert!(!result.is_zero());
 72            Ok(Self::new(F::constant(result.x), F::constant(result.y)))
 73        } else {
 74            let (x1, y1) = (&self.x, &self.y);
 75            let x1_sqr = x1.square()?;
 76            // Then,
 77            // tangent lambda := (3 * x1^2 + a) / (2 * y1);
 78            // x3 = lambda^2 - 2x1
 79            // y3 = lambda * (x1 - x3) - y1
 80            let numerator = x1_sqr.double()? + &x1_sqr + P::COEFF_A;
 81            let denominator = y1.double()?;
 82            let lambda = numerator.mul_by_inverse(&denominator)?;
 83            let x3 = lambda.square()? - x1.double()?;
 84            let y3 = lambda * &(x1 - &x3) - y1;
 85            Ok(Self::new(x3, y3))
 86        }
 87    }
 88
 89    /// Computes `(self + other) + self`. This method requires only 5 constraints,
 90    /// less than the 7 required when computing via `self.double() + other`.
 91    ///
 92    /// This follows the formulae from [\[ELM03\]](https://arxiv.org/abs/math/0208038).
 93    #[tracing::instrument(target = "r1cs", skip(self))]
 94    pub(crate) fn double_and_add(&self, other: &Self) -> Result<Self, SynthesisError> {
 95        if [self].is_constant() || other.is_constant() {
 96            self.double()?.add_unchecked(other)
 97        } else {
 98            let (x1, y1) = (&self.x, &self.y);
 99            let (x2, y2) = (&other.x, &other.y);
100
101            // Calculate self + other:
102            // slope lambda := (y2 - y1)/(x2 - x1);
103            // x3 = lambda^2 - x1 - x2;
104            // y3 = lambda * (x1 - x3) - y1
105            let numerator = y2 - y1;
106            let denominator = x2 - x1;
107            let lambda_1 = numerator.mul_by_inverse(&denominator)?;
108
109            let x3 = lambda_1.square()? - x1 - x2;
110
111            // Calculate final addition slope:
112            let lambda_2 = (lambda_1 + y1.double()?.mul_by_inverse(&(&x3 - x1))?).negate()?;
113
114            let x4 = lambda_2.square()? - x1 - x3;
115            let y4 = lambda_2 * &(x1 - &x4) - y1;
116            Ok(Self::new(x4, y4))
117        }
118    }
119
120    /// Doubles `self` in place.
121    #[tracing::instrument(target = "r1cs", skip(self))]
122    pub(crate) fn double_in_place(&mut self) -> Result<(), SynthesisError> {
123        *self = self.double()?;
124        Ok(())
125    }
126}
127
128impl<P, F> R1CSVar<<P::BaseField as Field>::BasePrimeField> for NonZeroAffineVar<P, F>
129where
130    P: SWModelParameters,
131    F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>,
132    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
133{
134    type Value = SWAffine<P>;
135
136    fn cs(&self) -> ConstraintSystemRef<<P::BaseField as Field>::BasePrimeField> {
137        self.x.cs().or(self.y.cs())
138    }
139
140    fn value(&self) -> Result<SWAffine<P>, SynthesisError> {
141        Ok(SWAffine::new(self.x.value()?, self.y.value()?, false))
142    }
143}
144
145impl<P, F> CondSelectGadget<<P::BaseField as Field>::BasePrimeField> for NonZeroAffineVar<P, F>
146where
147    P: SWModelParameters,
148    F: FieldVar<P::BaseField, <P::BaseField as Field>::BasePrimeField>,
149    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
150{
151    #[inline]
152    #[tracing::instrument(target = "r1cs")]
153    fn conditionally_select(
154        cond: &Boolean<<P::BaseField as Field>::BasePrimeField>,
155        true_value: &Self,
156        false_value: &Self,
157    ) -> Result<Self, SynthesisError> {
158        let x = cond.select(&true_value.x, &false_value.x)?;
159        let y = cond.select(&true_value.y, &false_value.y)?;
160
161        Ok(Self::new(x, y))
162    }
163}