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}