Skip to main content

ark_r1cs_std/groups/curves/short_weierstrass/
mod.rs

1use ark_ec::{
2    short_weierstrass::{Affine as SWAffine, Projective as SWProjective, SWCurveConfig},
3    AffineRepr, CurveConfig, CurveGroup,
4};
5use ark_ff::{AdditiveGroup, BitIteratorBE, Field, One, PrimeField, Zero};
6use ark_relations::gr1cs::{ConstraintSystemRef, Namespace, SynthesisError};
7use ark_std::{borrow::Borrow, marker::PhantomData, ops::Mul};
8use educe::Educe;
9use non_zero_affine::NonZeroAffineVar;
10
11use crate::{
12    convert::ToConstraintFieldGadget,
13    fields::{emulated_fp::EmulatedFpVar, fp::FpVar},
14    prelude::*,
15    Vec,
16};
17
18/// This module provides a generic implementation of G1 and G2 for
19/// the [\[BLS12]\](<https://eprint.iacr.org/2002/088.pdf>) family of bilinear groups.
20pub mod bls12;
21
22/// This module provides a generic implementation of G1 and G2 for
23/// the [\[MNT4]\](<https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.20.8113&rep=rep1&type=pdf>)
24///  family of bilinear groups.
25pub mod mnt4;
26/// This module provides a generic implementation of G1 and G2 for
27/// the [\[MNT6]\](<https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.20.8113&rep=rep1&type=pdf>)
28///  family of bilinear groups.
29pub mod mnt6;
30
31/// This module provides a generic implementation of elliptic curve operations
32/// for points on short-weierstrass curves in affine coordinates that **are
33/// not** equal to zero.
34///
35/// Note: this module is **unsafe** in general: it can synthesize unsatisfiable
36/// or underconstrained constraint systems when a represented point _is_ equal
37/// to zero. The [ProjectiveVar] gadget is the recommended way of working with
38/// elliptic curve points.
39pub mod non_zero_affine;
40
41type BasePrimeField<P> = <<P as CurveConfig>::BaseField as Field>::BasePrimeField;
42
43/// An implementation of arithmetic for Short Weierstrass curves that relies on
44/// the complete formulae derived in the paper of
45/// [[Renes, Costello, Batina 2015]](<https://eprint.iacr.org/2015/1060>).
46#[derive(Educe)]
47#[educe(Debug, Clone)]
48#[must_use]
49pub struct ProjectiveVar<P: SWCurveConfig, F: FieldVar<P::BaseField, BasePrimeField<P>>>
50where
51    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
52{
53    /// The x-coordinate.
54    pub x: F,
55    /// The y-coordinate.
56    pub y: F,
57    /// The z-coordinate.
58    pub z: F,
59    #[educe(Debug(ignore))]
60    _params: PhantomData<P>,
61}
62
63/// An affine representation of a curve point.
64#[derive(Educe)]
65#[educe(Debug, Clone)]
66#[must_use]
67pub struct AffineVar<P: SWCurveConfig, F: FieldVar<P::BaseField, BasePrimeField<P>>>
68where
69    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
70{
71    /// The x-coordinate.
72    pub x: F,
73    /// The y-coordinate.
74    pub y: F,
75    /// Is `self` the point at infinity.
76    pub infinity: Boolean<BasePrimeField<P>>,
77    #[educe(Debug(ignore))]
78    _params: PhantomData<P>,
79}
80
81impl<P, F> AffineVar<P, F>
82where
83    P: SWCurveConfig,
84    F: FieldVar<P::BaseField, BasePrimeField<P>>,
85    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
86{
87    fn new(x: F, y: F, infinity: Boolean<BasePrimeField<P>>) -> Self {
88        Self {
89            x,
90            y,
91            infinity,
92            _params: PhantomData,
93        }
94    }
95
96    /// Returns the value assigned to `self` in the underlying
97    /// constraint system.
98    pub fn value(&self) -> Result<SWAffine<P>, SynthesisError> {
99        Ok(match self.infinity.value()? {
100            true => SWAffine::identity(),
101            false => SWAffine::new(self.x.value()?, self.y.value()?),
102        })
103    }
104}
105
106impl<P, F> ToConstraintFieldGadget<BasePrimeField<P>> for AffineVar<P, F>
107where
108    P: SWCurveConfig,
109    F: FieldVar<P::BaseField, BasePrimeField<P>>,
110    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
111    F: ToConstraintFieldGadget<BasePrimeField<P>>,
112{
113    fn to_constraint_field(&self) -> Result<Vec<FpVar<BasePrimeField<P>>>, SynthesisError> {
114        let mut res = Vec::<FpVar<BasePrimeField<P>>>::new();
115
116        res.extend_from_slice(&self.x.to_constraint_field()?);
117        res.extend_from_slice(&self.y.to_constraint_field()?);
118        res.extend_from_slice(&self.infinity.to_constraint_field()?);
119
120        Ok(res)
121    }
122}
123
124impl<P, F> GR1CSVar<BasePrimeField<P>> for ProjectiveVar<P, F>
125where
126    P: SWCurveConfig,
127    F: FieldVar<P::BaseField, BasePrimeField<P>>,
128    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
129{
130    type Value = SWProjective<P>;
131
132    fn cs(&self) -> ConstraintSystemRef<BasePrimeField<P>> {
133        self.x.cs().or(self.y.cs()).or(self.z.cs())
134    }
135
136    fn value(&self) -> Result<Self::Value, SynthesisError> {
137        let (x, y, z) = (self.x.value()?, self.y.value()?, self.z.value()?);
138        let result = if let Some(z_inv) = z.inverse() {
139            SWAffine::new(x * &z_inv, y * &z_inv)
140        } else {
141            SWAffine::identity()
142        };
143        Ok(result.into())
144    }
145}
146
147impl<P: SWCurveConfig, F: FieldVar<P::BaseField, BasePrimeField<P>>> ProjectiveVar<P, F>
148where
149    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
150{
151    /// Constructs `Self` from an `(x, y, z)` coordinate triple.
152    pub fn new(x: F, y: F, z: F) -> Self {
153        Self {
154            x,
155            y,
156            z,
157            _params: PhantomData,
158        }
159    }
160
161    /// Convert this point into affine form.
162    #[tracing::instrument(target = "gr1cs")]
163    pub fn to_affine(&self) -> Result<AffineVar<P, F>, SynthesisError> {
164        if self.is_constant() {
165            let point = self.value()?.into_affine();
166            let x = F::new_constant(ConstraintSystemRef::None, point.x)?;
167            let y = F::new_constant(ConstraintSystemRef::None, point.y)?;
168            let infinity = Boolean::constant(point.is_zero());
169            Ok(AffineVar::new(x, y, infinity))
170        } else {
171            let cs = self.cs();
172            let infinity = self.is_zero()?;
173            let zero_affine = SWAffine::<P>::zero();
174            let zero_x = F::new_constant(cs.clone(), &zero_affine.x)?;
175            let zero_y = F::new_constant(cs.clone(), &zero_affine.y)?;
176            // Allocate a variable whose value is either `self.z.inverse()` if the inverse
177            // exists, and is zero otherwise.
178            let z_inv = F::new_witness(ark_relations::ns!(cs, "z_inverse"), || {
179                Ok(self.z.value()?.inverse().unwrap_or_else(P::BaseField::zero))
180            })?;
181            // The inverse exists if `!self.is_zero()`.
182            // This means that `z_inv * self.z = 1` if `self.is_not_zero()`, and
183            //                 `z_inv * self.z = 0` if `self.is_zero()`.
184            //
185            // Thus, `z_inv * self.z = !self.is_zero()`.
186            z_inv.mul_equals(&self.z, &F::from(!&infinity))?;
187
188            let non_zero_x = &self.x * &z_inv;
189            let non_zero_y = &self.y * &z_inv;
190
191            let x = infinity.select(&zero_x, &non_zero_x)?;
192            let y = infinity.select(&zero_y, &non_zero_y)?;
193
194            Ok(AffineVar::new(x, y, infinity))
195        }
196    }
197
198    /// Allocates a new variable without performing an on-curve check, which is
199    /// useful if the variable is known to be on the curve (eg., if the point
200    /// is a constant or is a public input).
201    #[tracing::instrument(target = "gr1cs", skip(cs, f))]
202    pub fn new_variable_omit_on_curve_check(
203        cs: impl Into<Namespace<BasePrimeField<P>>>,
204        f: impl FnOnce() -> Result<SWProjective<P>, SynthesisError>,
205        mode: AllocationMode,
206    ) -> Result<Self, SynthesisError> {
207        let ns = cs.into();
208        let cs = ns.cs();
209
210        let (x, y, z) = match f() {
211            Ok(ge) => {
212                let ge = ge.into_affine();
213                if ge.is_zero() {
214                    // These values are convenient since the point satisfies
215                    // curve equation.
216                    (
217                        Ok(P::BaseField::zero()),
218                        Ok(P::BaseField::one()),
219                        Ok(P::BaseField::zero()),
220                    )
221                } else {
222                    (Ok(ge.x), Ok(ge.y), Ok(P::BaseField::one()))
223                }
224            },
225            _ => (
226                Err(SynthesisError::AssignmentMissing),
227                Err(SynthesisError::AssignmentMissing),
228                Err(SynthesisError::AssignmentMissing),
229            ),
230        };
231
232        let x = F::new_variable(ark_relations::ns!(cs, "x"), || x, mode)?;
233        let y = F::new_variable(ark_relations::ns!(cs, "y"), || y, mode)?;
234        let z = F::new_variable(ark_relations::ns!(cs, "z"), || z, mode)?;
235
236        Ok(Self::new(x, y, z))
237    }
238
239    /// Mixed addition, which is useful when `other = (x2, y2)` is known to have
240    /// z = 1.
241    #[tracing::instrument(target = "gr1cs", skip(self, other))]
242    pub(crate) fn add_mixed(&self, other: &NonZeroAffineVar<P, F>) -> Result<Self, SynthesisError> {
243        // Complete mixed addition formula from Renes-Costello-Batina 2015
244        // Algorithm 2
245        // (https://eprint.iacr.org/2015/1060).
246        // Below, comments at the end of a line denote the corresponding
247        // step(s) of the algorithm
248        //
249        // Adapted from code in
250        // https://github.com/RustCrypto/elliptic-curves/blob/master/p256/src/arithmetic/projective.rs
251        let three_b = P::COEFF_B.double() + &P::COEFF_B;
252        let (x1, y1, z1) = (&self.x, &self.y, &self.z);
253        let (x2, y2) = (&other.x, &other.y);
254
255        let xx = x1 * x2; // 1
256        let yy = y1 * y2; // 2
257        let xy_pairs = ((x1 + y1) * &(x2 + y2)) - (&xx + &yy); // 4, 5, 6, 7, 8
258        let xz_pairs = (x2 * z1) + x1; // 8, 9
259        let yz_pairs = (y2 * z1) + y1; // 10, 11
260
261        let axz = mul_by_coeff_a::<P, F>(&xz_pairs); // 12
262
263        let bz3_part = &axz + z1 * three_b; // 13, 14
264
265        let yy_m_bz3 = &yy - &bz3_part; // 15
266        let yy_p_bz3 = &yy + &bz3_part; // 16
267
268        let azz = mul_by_coeff_a::<P, F>(z1); // 20
269        let xx3_p_azz = xx.double().unwrap() + &xx + &azz; // 18, 19, 22
270
271        let bxz3 = &xz_pairs * three_b; // 21
272        let b3_xz_pairs = mul_by_coeff_a::<P, F>(&(&xx - &azz)) + &bxz3; // 23, 24, 25
273
274        let x = (&yy_m_bz3 * &xy_pairs) - &yz_pairs * &b3_xz_pairs; // 28,29, 30
275        let y = (&yy_p_bz3 * &yy_m_bz3) + &xx3_p_azz * b3_xz_pairs; // 17, 26, 27
276        let z = (&yy_p_bz3 * &yz_pairs) + xy_pairs * xx3_p_azz; // 31, 32, 33
277
278        Ok(ProjectiveVar::new(x, y, z))
279    }
280
281    /// Computes a scalar multiplication with a little-endian scalar of size
282    /// `P::ScalarField::MODULUS_BITS`.
283    #[tracing::instrument(
284        target = "gr1cs",
285        skip(self, mul_result, multiple_of_power_of_two, bits)
286    )]
287    fn fixed_scalar_mul_le(
288        &self,
289        mul_result: &mut Self,
290        multiple_of_power_of_two: &mut NonZeroAffineVar<P, F>,
291        bits: &[&Boolean<BasePrimeField<P>>],
292    ) -> Result<(), SynthesisError> {
293        let scalar_modulus_bits = <P::ScalarField as PrimeField>::MODULUS_BIT_SIZE as usize;
294
295        assert!(scalar_modulus_bits >= bits.len());
296        let split_len = ark_std::cmp::min(scalar_modulus_bits - 2, bits.len());
297        let (affine_bits, proj_bits) = bits.split_at(split_len);
298        // Computes the standard little-endian double-and-add algorithm
299        // (Algorithm 3.26, Guide to Elliptic Curve Cryptography)
300        //
301        // We rely on *incomplete* affine formulae for partially computing this.
302        // However, we avoid exceptional edge cases because we partition the scalar
303        // into two chunks: one guaranteed to be less than p - 2, and the rest.
304        // We only use incomplete formulae for the first chunk, which means we avoid
305        // exceptions:
306        //
307        // `add_unchecked(a, b)` is incomplete when either `b.is_zero()`, or when
308        // `b = ±a`. During scalar multiplication, we don't hit either case:
309        // * `b = ±a`: `b = accumulator = k * a`, where `2 <= k < p - 1`. This implies
310        //   that `k != p ± 1`, and so `b != (p ± 1) * a`. Because the group is finite,
311        //   this in turn means that `b != ±a`, as required.
312        // * `a` or `b` is zero: for `a`, we handle the zero case after the loop; for
313        //   `b`, notice that it is monotonically increasing, and furthermore, equals `k
314        //   * a`, where `k != p = 0 mod p`.
315
316        // Unlike normal double-and-add, here we start off with a non-zero
317        // `accumulator`, because `NonZeroAffineVar::add_unchecked` doesn't
318        // support addition with `zero`. In more detail, we initialize
319        // `accumulator` to be the initial value of `multiple_of_power_of_two`.
320        // This ensures that all unchecked additions of `accumulator` with later
321        // values of `multiple_of_power_of_two` are safe. However, to do this
322        // correctly, we need to perform two steps:
323        // * We must skip the LSB, and instead proceed assuming that it was 1. Later, we
324        //   will conditionally subtract the initial value of `accumulator`: if LSB ==
325        //   0: subtract initial_acc_value; else, subtract 0.
326        // * Because we are assuming the first bit, we must double
327        //   `multiple_of_power_of_two`.
328
329        let mut accumulator = multiple_of_power_of_two.clone();
330        let initial_acc_value = accumulator.into_projective();
331
332        // The powers start at 2 (instead of 1) because we're skipping the first bit.
333        multiple_of_power_of_two.double_in_place()?;
334
335        // As mentioned, we will skip the LSB, and will later handle it via a
336        // conditional subtraction.
337        for bit in affine_bits.iter().skip(1) {
338            if bit.is_constant() {
339                if *bit == &Boolean::TRUE {
340                    accumulator = accumulator.add_unchecked(multiple_of_power_of_two)?;
341                }
342            } else {
343                let temp = accumulator.add_unchecked(multiple_of_power_of_two)?;
344                accumulator = bit.select(&temp, &accumulator)?;
345            }
346            multiple_of_power_of_two.double_in_place()?;
347        }
348        // Perform conditional subtraction:
349
350        // We can convert to projective safely because the result is guaranteed to be
351        // non-zero by the condition on `affine_bits.len()`, and by the fact
352        // that `accumulator` is non-zero
353        let result = accumulator.into_projective();
354        // If bits[0] is 0, then we have to subtract `self`; else, we subtract zero.
355        let subtrahend = bits[0].select(&Self::zero(), &initial_acc_value)?;
356        *mul_result += result - subtrahend;
357
358        // Now, let's finish off the rest of the bits using our complete formulae
359        for bit in proj_bits {
360            if bit.is_constant() {
361                if *bit == &Boolean::TRUE {
362                    *mul_result += &multiple_of_power_of_two.into_projective();
363                }
364            } else {
365                let temp = &*mul_result + &multiple_of_power_of_two.into_projective();
366                *mul_result = bit.select(&temp, &mul_result)?;
367            }
368            multiple_of_power_of_two.double_in_place()?;
369        }
370        Ok(())
371    }
372}
373
374impl<P, F> CurveVar<SWProjective<P>, BasePrimeField<P>> for ProjectiveVar<P, F>
375where
376    P: SWCurveConfig,
377    F: FieldVar<P::BaseField, BasePrimeField<P>>,
378    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
379{
380    type BaseFieldVar = F;
381
382    fn constant(g: SWProjective<P>) -> Self {
383        let cs = ConstraintSystemRef::None;
384        Self::new_variable_omit_on_curve_check(cs, || Ok(g), AllocationMode::Constant).unwrap()
385    }
386
387    fn zero() -> Self {
388        Self::new(F::zero(), F::one(), F::zero())
389    }
390
391    fn is_zero(&self) -> Result<Boolean<BasePrimeField<P>>, SynthesisError> {
392        self.z.is_zero()
393    }
394
395    #[tracing::instrument(target = "gr1cs", skip(cs, f))]
396    fn new_variable_omit_prime_order_check(
397        cs: impl Into<Namespace<BasePrimeField<P>>>,
398        f: impl FnOnce() -> Result<SWProjective<P>, SynthesisError>,
399        mode: AllocationMode,
400    ) -> Result<Self, SynthesisError> {
401        let ns = cs.into();
402        let cs = ns.cs();
403        // Curve equation in projective form:
404        // E: Y² * Z = X³ + aX * Z² + bZ³
405        //
406        // This can be re-written as
407        // E: Y² * Z - bZ³ = X³ + aX * Z²
408        // E: Z * (Y² - bZ²) = X * (X² + aZ²)
409        // so, compute X², Y², Z²,
410        //     compute temp = X * (X² + aZ²)
411        //     check Z.mul_equals((Y² - bZ²), temp)
412        //
413        //     A total of 5 multiplications
414
415        let g = Self::new_variable_omit_on_curve_check(cs, f, mode)?;
416
417        if mode != AllocationMode::Constant {
418            // Perform on-curve check.
419            let b = P::COEFF_B;
420            let a = P::COEFF_A;
421
422            let x2 = g.x.square()?;
423            let y2 = g.y.square()?;
424            let z2 = g.z.square()?;
425            let t = &g.x * (x2 + &z2 * a);
426
427            g.z.mul_equals(&(y2 - z2 * b), &t)?;
428        }
429        Ok(g)
430    }
431
432    /// Enforce that `self` is in the prime-order subgroup.
433    ///
434    /// Does so by multiplying by the prime order, and checking that the result
435    /// is unchanged.
436    // TODO: at the moment this doesn't work, because the addition and doubling
437    // formulae are incomplete for even-order points.
438    #[tracing::instrument(target = "gr1cs")]
439    fn enforce_prime_order(&self) -> Result<(), SynthesisError> {
440        unimplemented!("cannot enforce prime order");
441        // let r_minus_1 = (-P::ScalarField::one()).into_bigint();
442
443        // let mut result = Self::zero();
444        // for b in BitIteratorBE::without_leading_zeros(r_minus_1) {
445        //     result.double_in_place()?;
446
447        //     if b {
448        //         result += self;
449        //     }
450        // }
451        // self.negate()?.enforce_equal(&result)?;
452        // Ok(())
453    }
454
455    #[inline]
456    #[tracing::instrument(target = "gr1cs")]
457    fn double_in_place(&mut self) -> Result<(), SynthesisError> {
458        // Complete doubling formula from Renes-Costello-Batina 2015
459        // Algorithm 3
460        // (https://eprint.iacr.org/2015/1060).
461        // Below, comments at the end of a line denote the corresponding
462        // step(s) of the algorithm
463        //
464        // Adapted from code in
465        // https://github.com/RustCrypto/elliptic-curves/blob/master/p256/src/arithmetic/projective.rs
466        let three_b = P::COEFF_B.double() + &P::COEFF_B;
467
468        let xx = self.x.square()?; // 1
469        let yy = self.y.square()?; // 2
470        let zz = self.z.square()?; // 3
471        let xy2 = (&self.x * &self.y).double()?; // 4, 5
472        let xz2 = (&self.x * &self.z).double()?; // 6, 7
473
474        let axz2 = mul_by_coeff_a::<P, F>(&xz2); // 8
475
476        let bzz3_part = &axz2 + &zz * three_b; // 9, 10
477        let yy_m_bzz3 = &yy - &bzz3_part; // 11
478        let yy_p_bzz3 = &yy + &bzz3_part; // 12
479        let y_frag = yy_p_bzz3 * &yy_m_bzz3; // 13
480        let x_frag = yy_m_bzz3 * &xy2; // 14
481
482        let bxz3 = xz2 * three_b; // 15
483        let azz = mul_by_coeff_a::<P, F>(&zz); // 16
484        let b3_xz_pairs = mul_by_coeff_a::<P, F>(&(&xx - &azz)) + &bxz3; // 15, 16, 17, 18, 19
485        let xx3_p_azz = (xx.double()? + &xx + &azz) * &b3_xz_pairs; // 23, 24, 25
486
487        let y = y_frag + &xx3_p_azz; // 26, 27
488        let yz2 = (&self.y * &self.z).double()?; // 28, 29
489        let x = x_frag - &(b3_xz_pairs * &yz2); // 30, 31
490        let z = (yz2 * &yy).double()?.double()?; // 32, 33, 34
491        self.x = x;
492        self.y = y;
493        self.z = z;
494        Ok(())
495    }
496
497    #[tracing::instrument(target = "gr1cs")]
498    fn negate(&self) -> Result<Self, SynthesisError> {
499        Ok(Self::new(self.x.clone(), self.y.negate()?, self.z.clone()))
500    }
501
502    /// Computes `bits * self`, where `bits` is a little-endian
503    /// `Boolean` representation of a scalar.
504    #[tracing::instrument(target = "gr1cs", skip(bits))]
505    fn scalar_mul_le<'a>(
506        &self,
507        bits: impl Iterator<Item = &'a Boolean<BasePrimeField<P>>>,
508    ) -> Result<Self, SynthesisError> {
509        if self.is_constant() && self.value().unwrap().is_zero() {
510            return Ok(self.clone());
511        }
512        let self_affine = self.to_affine()?;
513        let (x, y, infinity) = (self_affine.x, self_affine.y, self_affine.infinity);
514        // We first handle the non-zero case, and then later will conditionally select
515        // zero if `self` was zero. However, we also want to make sure that generated
516        // constraints are satisfiable in both cases.
517        //
518        // In particular, using non-sensible values for `x` and `y` in zero-case may
519        // cause `unchecked` operations to generate constraints that can never
520        // be satisfied, depending on the curve equation coefficients.
521        //
522        // The safest approach is to use coordinates of some point from the curve, thus
523        // not violating assumptions of `NonZeroAffine`. For instance, generator
524        // point.
525        let x = infinity.select(&F::constant(P::GENERATOR.x), &x)?;
526        let y = infinity.select(&F::constant(P::GENERATOR.y), &y)?;
527        let non_zero_self = NonZeroAffineVar::new(x, y);
528
529        let mut bits = bits.collect::<Vec<_>>();
530        if bits.len() == 0 {
531            return Ok(Self::zero());
532        }
533        // Remove unnecessary constant zeros in the most-significant positions.
534        bits = bits
535            .into_iter()
536            // We iterate from the MSB down.
537            .rev()
538            // Skip leading zeros, if they are constants.
539            .skip_while(|b| b.is_constant() && (b.value().unwrap() == false))
540            .collect();
541        // After collecting we are in big-endian form; we have to reverse to get back to
542        // little-endian.
543        bits.reverse();
544
545        let scalar_modulus_bits = <P::ScalarField as PrimeField>::MODULUS_BIT_SIZE;
546        let mut mul_result = Self::zero();
547        let mut power_of_two_times_self = non_zero_self;
548        // We chunk up `bits` into `p`-sized chunks.
549        for bits in bits.chunks(scalar_modulus_bits as usize) {
550            self.fixed_scalar_mul_le(&mut mul_result, &mut power_of_two_times_self, bits)?;
551        }
552
553        // The foregoing algorithm relies on incomplete addition, and so does not
554        // work when the input (`self`) is zero. We hence have to perform
555        // a check to ensure that if the input is zero, then so is the output.
556        // The cost of this check should be less than the benefit of using
557        // mixed addition in almost all cases.
558        infinity.select(&Self::zero(), &mul_result)
559    }
560
561    #[tracing::instrument(target = "gr1cs", skip(scalar_bits_with_bases))]
562    fn precomputed_base_scalar_mul_le<'a, I, B>(
563        &mut self,
564        scalar_bits_with_bases: I,
565    ) -> Result<(), SynthesisError>
566    where
567        I: Iterator<Item = (B, &'a SWProjective<P>)>,
568        B: Borrow<Boolean<BasePrimeField<P>>>,
569    {
570        // We just ignore the provided bases and use the faster scalar multiplication.
571        let (bits, bases): (Vec<_>, Vec<_>) = scalar_bits_with_bases
572            .map(|(b, c)| (b.borrow().clone(), *c))
573            .unzip();
574        let base = bases[0];
575        *self += Self::constant(base).scalar_mul_le(bits.iter())?;
576        Ok(())
577    }
578
579    fn affine_xy(&self) -> Result<(F, F), SynthesisError> {
580        let self_affine = self.to_affine()?;
581        Ok((self_affine.x, self_affine.y))
582    }
583}
584
585impl<P, F> ToConstraintFieldGadget<BasePrimeField<P>> for ProjectiveVar<P, F>
586where
587    P: SWCurveConfig,
588    F: FieldVar<P::BaseField, BasePrimeField<P>>,
589    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
590    F: ToConstraintFieldGadget<BasePrimeField<P>>,
591{
592    fn to_constraint_field(&self) -> Result<Vec<FpVar<BasePrimeField<P>>>, SynthesisError> {
593        self.to_affine()?.to_constraint_field()
594    }
595}
596
597fn mul_by_coeff_a<P: SWCurveConfig, F: FieldVar<P::BaseField, BasePrimeField<P>>>(f: &F) -> F
598where
599    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
600{
601    if !P::COEFF_A.is_zero() {
602        f * P::COEFF_A
603    } else {
604        F::zero()
605    }
606}
607
608impl_bounded_ops!(
609    ProjectiveVar<P, F>,
610    SWProjective<P>,
611    Add,
612    add,
613    AddAssign,
614    add_assign,
615    |mut this: &'a ProjectiveVar<P, F>, mut other: &'a ProjectiveVar<P, F>| {
616        // Implement complete addition for Short Weierstrass curves, following
617        // the complete addition formula from Renes-Costello-Batina 2015
618        // (https://eprint.iacr.org/2015/1060).
619        //
620        // We special case handling of constants to get better constraint weight.
621        if this.is_constant() {
622            // we'll just act like `other` is constant.
623            core::mem::swap(&mut this, &mut other);
624        }
625
626        if other.is_constant() {
627            // The value should exist because `other` is a constant.
628            let other = other.value().unwrap();
629            if other.is_zero() {
630                // this + 0 = this
631                this.clone()
632            } else {
633                // We'll use mixed addition to add non-zero constants.
634                let x = F::constant(other.x);
635                let y = F::constant(other.y);
636                this.add_mixed(&NonZeroAffineVar::new(x, y)).unwrap()
637            }
638        } else {
639            // Complete addition formula from Renes-Costello-Batina 2015
640            // Algorithm 1
641            // (https://eprint.iacr.org/2015/1060).
642            // Below, comments at the end of a line denote the corresponding
643            // step(s) of the algorithm
644            //
645            // Adapted from code in
646            // https://github.com/RustCrypto/elliptic-curves/blob/master/p256/src/arithmetic/projective.rs
647            let three_b = P::COEFF_B.double() + &P::COEFF_B;
648            let (x1, y1, z1) = (&this.x, &this.y, &this.z);
649            let (x2, y2, z2) = (&other.x, &other.y, &other.z);
650
651            let xx = x1 * x2; // 1
652            let yy = y1 * y2; // 2
653            let zz = z1 * z2; // 3
654            let xy_pairs = ((x1 + y1) * &(x2 + y2)) - (&xx + &yy); // 4, 5, 6, 7, 8
655            let xz_pairs = ((x1 + z1) * &(x2 + z2)) - (&xx + &zz); // 9, 10, 11, 12, 13
656            let yz_pairs = ((y1 + z1) * &(y2 + z2)) - (&yy + &zz); // 14, 15, 16, 17, 18
657
658            let axz = mul_by_coeff_a::<P, F>(&xz_pairs); // 19
659
660            let bzz3_part = &axz + &zz * three_b; // 20, 21
661
662            let yy_m_bzz3 = &yy - &bzz3_part; // 22
663            let yy_p_bzz3 = &yy + &bzz3_part; // 23
664
665            let azz = mul_by_coeff_a::<P, F>(&zz);
666            let xx3_p_azz = xx.double().unwrap() + &xx + &azz; // 25, 26, 27, 29
667
668            let bxz3 = &xz_pairs * three_b; // 28
669            let b3_xz_pairs = mul_by_coeff_a::<P, F>(&(&xx - &azz)) + &bxz3; // 30, 31, 32
670
671            let x = (&yy_m_bzz3 * &xy_pairs) - &yz_pairs * &b3_xz_pairs; // 35, 39, 40
672            let y = (&yy_p_bzz3 * &yy_m_bzz3) + &xx3_p_azz * b3_xz_pairs; // 24, 36, 37, 38
673            let z = (&yy_p_bzz3 * &yz_pairs) + xy_pairs * xx3_p_azz; // 41, 42, 43
674
675            ProjectiveVar::new(x, y, z)
676        }
677
678    },
679    |this: &'a ProjectiveVar<P, F>, other: SWProjective<P>| {
680        this + ProjectiveVar::constant(other)
681    },
682    (F: FieldVar<P::BaseField, BasePrimeField<P>>, P: SWCurveConfig),
683    for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>,
684);
685
686impl_bounded_ops!(
687    ProjectiveVar<P, F>,
688    SWProjective<P>,
689    Sub,
690    sub,
691    SubAssign,
692    sub_assign,
693    |this: &'a ProjectiveVar<P, F>, other: &'a ProjectiveVar<P, F>| this + other.negate().unwrap(),
694    |this: &'a ProjectiveVar<P, F>, other: SWProjective<P>| this - ProjectiveVar::constant(other),
695    (F: FieldVar<P::BaseField, BasePrimeField<P>>, P: SWCurveConfig),
696    for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>
697);
698
699impl_bounded_ops_diff!(
700    ProjectiveVar<P, F>,
701    SWProjective<P>,
702    EmulatedFpVar<P::ScalarField, BasePrimeField<P>>,
703    P::ScalarField,
704    Mul,
705    mul,
706    MulAssign,
707    mul_assign,
708    |this: &'a ProjectiveVar<P, F>, other: &'a EmulatedFpVar<P::ScalarField, BasePrimeField<P>>| {
709        if this.is_constant() && other.is_constant() {
710            assert!(this.is_constant() && other.is_constant());
711            ProjectiveVar::constant(this.value().unwrap() * &other.value().unwrap())
712        } else {
713            let bits = other.to_bits_le().unwrap();
714            this.scalar_mul_le(bits.iter()).unwrap()
715        }
716    },
717    |this: &'a ProjectiveVar<P, F>, other: P::ScalarField| this * EmulatedFpVar::constant(other),
718    (F: FieldVar<P::BaseField, BasePrimeField<P>>, P: SWCurveConfig),
719    for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>,
720);
721
722impl<'a, P, F> GroupOpsBounds<'a, SWProjective<P>, ProjectiveVar<P, F>> for ProjectiveVar<P, F>
723where
724    P: SWCurveConfig,
725    F: FieldVar<P::BaseField, BasePrimeField<P>>,
726    for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>,
727{
728}
729
730impl<'a, P, F> GroupOpsBounds<'a, SWProjective<P>, ProjectiveVar<P, F>> for &'a ProjectiveVar<P, F>
731where
732    P: SWCurveConfig,
733    F: FieldVar<P::BaseField, BasePrimeField<P>>,
734    for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>,
735{
736}
737
738impl<P, F> CondSelectGadget<BasePrimeField<P>> for ProjectiveVar<P, F>
739where
740    P: SWCurveConfig,
741    F: FieldVar<P::BaseField, BasePrimeField<P>>,
742    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
743{
744    #[inline]
745    #[tracing::instrument(target = "gr1cs")]
746    fn conditionally_select(
747        cond: &Boolean<BasePrimeField<P>>,
748        true_value: &Self,
749        false_value: &Self,
750    ) -> Result<Self, SynthesisError> {
751        let x = cond.select(&true_value.x, &false_value.x)?;
752        let y = cond.select(&true_value.y, &false_value.y)?;
753        let z = cond.select(&true_value.z, &false_value.z)?;
754
755        Ok(Self::new(x, y, z))
756    }
757}
758
759impl<P, F> EqGadget<BasePrimeField<P>> for ProjectiveVar<P, F>
760where
761    P: SWCurveConfig,
762    F: FieldVar<P::BaseField, BasePrimeField<P>>,
763    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
764{
765    #[tracing::instrument(target = "gr1cs")]
766    fn is_eq(&self, other: &Self) -> Result<Boolean<BasePrimeField<P>>, SynthesisError> {
767        let x_equal = (&self.x * &other.z).is_eq(&(&other.x * &self.z))?;
768        let y_equal = (&self.y * &other.z).is_eq(&(&other.y * &self.z))?;
769        let coordinates_equal = x_equal & y_equal;
770        let both_are_zero = self.is_zero()? & other.is_zero()?;
771        Ok(both_are_zero | coordinates_equal)
772    }
773
774    #[inline]
775    #[tracing::instrument(target = "gr1cs")]
776    fn conditional_enforce_equal(
777        &self,
778        other: &Self,
779        condition: &Boolean<BasePrimeField<P>>,
780    ) -> Result<(), SynthesisError> {
781        let x_equal = (&self.x * &other.z).is_eq(&(&other.x * &self.z))?;
782        let y_equal = (&self.y * &other.z).is_eq(&(&other.y * &self.z))?;
783        let coordinates_equal = x_equal & y_equal;
784        let both_are_zero = self.is_zero()? & other.is_zero()?;
785        (both_are_zero | coordinates_equal).conditional_enforce_equal(&Boolean::TRUE, condition)
786    }
787
788    #[inline]
789    #[tracing::instrument(target = "gr1cs")]
790    fn conditional_enforce_not_equal(
791        &self,
792        other: &Self,
793        condition: &Boolean<BasePrimeField<P>>,
794    ) -> Result<(), SynthesisError> {
795        let is_equal = self.is_eq(other)?;
796        (is_equal & condition).enforce_equal(&Boolean::FALSE)
797    }
798}
799
800impl<P, F> AllocVar<SWAffine<P>, BasePrimeField<P>> for ProjectiveVar<P, F>
801where
802    P: SWCurveConfig,
803    F: FieldVar<P::BaseField, BasePrimeField<P>>,
804    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
805{
806    fn new_variable<T: Borrow<SWAffine<P>>>(
807        cs: impl Into<Namespace<BasePrimeField<P>>>,
808        f: impl FnOnce() -> Result<T, SynthesisError>,
809        mode: AllocationMode,
810    ) -> Result<Self, SynthesisError> {
811        Self::new_variable(
812            cs,
813            || f().map(|b| SWProjective::from((*b.borrow()).clone())),
814            mode,
815        )
816    }
817}
818
819impl<P, F> AllocVar<SWProjective<P>, BasePrimeField<P>> for ProjectiveVar<P, F>
820where
821    P: SWCurveConfig,
822    F: FieldVar<P::BaseField, BasePrimeField<P>>,
823    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
824{
825    fn new_variable<T: Borrow<SWProjective<P>>>(
826        cs: impl Into<Namespace<BasePrimeField<P>>>,
827        f: impl FnOnce() -> Result<T, SynthesisError>,
828        mode: AllocationMode,
829    ) -> Result<Self, SynthesisError> {
830        let ns = cs.into();
831        let cs = ns.cs();
832        let f = || Ok(*f()?.borrow());
833        match mode {
834            AllocationMode::Constant => Self::new_variable_omit_prime_order_check(cs, f, mode),
835            AllocationMode::Input => Self::new_variable_omit_prime_order_check(cs, f, mode),
836            AllocationMode::Witness => {
837                // if cofactor.is_even():
838                //   divide until you've removed all even factors
839                // else:
840                //   just directly use double and add.
841                let mut power_of_2: u32 = 0;
842                let mut cofactor = P::COFACTOR.to_vec();
843                while cofactor[0] % 2 == 0 {
844                    div2(&mut cofactor);
845                    power_of_2 += 1;
846                }
847
848                let cofactor_weight = BitIteratorBE::new(cofactor.as_slice())
849                    .filter(|b| *b)
850                    .count();
851                let modulus_minus_1 = (-P::ScalarField::one()).into_bigint(); // r - 1
852                let modulus_minus_1_weight =
853                    BitIteratorBE::new(modulus_minus_1).filter(|b| *b).count();
854
855                // We pick the most efficient method of performing the prime order check:
856                // If the cofactor has lower hamming weight than the scalar field's modulus,
857                // we first multiply by the inverse of the cofactor, and then, after allocating,
858                // multiply by the cofactor. This ensures the resulting point has no cofactors
859                //
860                // Else, we multiply by the scalar field's modulus and ensure that the result
861                // equals the identity.
862
863                let (mut ge, iter) = if cofactor_weight < modulus_minus_1_weight {
864                    let ge = Self::new_variable_omit_prime_order_check(
865                        ark_relations::ns!(cs, "Witness without subgroup check with cofactor mul"),
866                        || f().map(|g| g.into_affine().mul_by_cofactor_inv().into()),
867                        mode,
868                    )?;
869                    (
870                        ge,
871                        BitIteratorBE::without_leading_zeros(cofactor.as_slice()),
872                    )
873                } else {
874                    let ge = Self::new_variable_omit_prime_order_check(
875                        ark_relations::ns!(cs, "Witness without subgroup check with `r` check"),
876                        || {
877                            f().map(|g| {
878                                let g = g.into_affine();
879                                let power_of_two = P::ScalarField::ONE.into_bigint() << power_of_2;
880                                let power_of_two_inv = P::ScalarField::from_bigint(power_of_two)
881                                    .and_then(|n| n.inverse())
882                                    .unwrap();
883                                g.mul(power_of_two_inv)
884                            })
885                        },
886                        mode,
887                    )?;
888
889                    (
890                        ge,
891                        BitIteratorBE::without_leading_zeros(modulus_minus_1.as_ref()),
892                    )
893                };
894                // Remove the even part of the cofactor
895                for _ in 0..power_of_2 {
896                    ge.double_in_place()?;
897                }
898
899                let mut result = Self::zero();
900                for b in iter {
901                    result.double_in_place()?;
902
903                    if b {
904                        result += &ge
905                    }
906                }
907                if cofactor_weight < modulus_minus_1_weight {
908                    Ok(result)
909                } else {
910                    ge.enforce_equal(&ge)?;
911                    Ok(ge)
912                }
913            },
914        }
915    }
916}
917
918#[inline]
919fn div2(limbs: &mut [u64]) {
920    let mut t = 0;
921    for i in limbs.iter_mut().rev() {
922        let t2 = *i << 63;
923        *i >>= 1;
924        *i |= t;
925        t = t2;
926    }
927}
928
929impl<P, F> ToBitsGadget<BasePrimeField<P>> for ProjectiveVar<P, F>
930where
931    P: SWCurveConfig,
932    F: FieldVar<P::BaseField, BasePrimeField<P>>,
933    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
934{
935    #[tracing::instrument(target = "gr1cs")]
936    fn to_bits_le(&self) -> Result<Vec<Boolean<BasePrimeField<P>>>, SynthesisError> {
937        let g = self.to_affine()?;
938        let mut bits = g.x.to_bits_le()?;
939        let y_bits = g.y.to_bits_le()?;
940        bits.extend_from_slice(&y_bits);
941        bits.push(g.infinity);
942        Ok(bits)
943    }
944
945    #[tracing::instrument(target = "gr1cs")]
946    fn to_non_unique_bits_le(&self) -> Result<Vec<Boolean<BasePrimeField<P>>>, SynthesisError> {
947        let g = self.to_affine()?;
948        let mut bits = g.x.to_non_unique_bits_le()?;
949        let y_bits = g.y.to_non_unique_bits_le()?;
950        bits.extend_from_slice(&y_bits);
951        bits.push(g.infinity);
952        Ok(bits)
953    }
954}
955
956impl<P, F> ToBytesGadget<BasePrimeField<P>> for ProjectiveVar<P, F>
957where
958    P: SWCurveConfig,
959    F: FieldVar<P::BaseField, BasePrimeField<P>>,
960    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
961{
962    #[tracing::instrument(target = "gr1cs")]
963    fn to_bytes_le(&self) -> Result<Vec<UInt8<BasePrimeField<P>>>, SynthesisError> {
964        let g = self.to_affine()?;
965        let mut bytes = g.x.to_bytes_le()?;
966        let y_bytes = g.y.to_bytes_le()?;
967        let inf_bytes = g.infinity.to_bytes_le()?;
968        bytes.extend_from_slice(&y_bytes);
969        bytes.extend_from_slice(&inf_bytes);
970        Ok(bytes)
971    }
972
973    #[tracing::instrument(target = "gr1cs")]
974    fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<BasePrimeField<P>>>, SynthesisError> {
975        let g = self.to_affine()?;
976        let mut bytes = g.x.to_non_unique_bytes_le()?;
977        let y_bytes = g.y.to_non_unique_bytes_le()?;
978        let inf_bytes = g.infinity.to_non_unique_bytes_le()?;
979        bytes.extend_from_slice(&y_bytes);
980        bytes.extend_from_slice(&inf_bytes);
981        Ok(bytes)
982    }
983}
984
985#[cfg(test)]
986mod test_sw_curve {
987    use crate::{
988        alloc::AllocVar,
989        convert::ToBitsGadget,
990        eq::EqGadget,
991        fields::{emulated_fp::EmulatedFpVar, fp::FpVar},
992        groups::{curves::short_weierstrass::ProjectiveVar, CurveVar},
993    };
994    use ark_ec::{
995        short_weierstrass::{Projective, SWCurveConfig},
996        CurveGroup,
997    };
998    use ark_ff::PrimeField;
999    use ark_relations::gr1cs::{ConstraintSystem, Result};
1000    use ark_std::UniformRand;
1001    use num_traits::Zero;
1002
1003    fn zero_point_scalar_mul_satisfied<G>() -> Result<bool>
1004    where
1005        G: CurveGroup,
1006        G::BaseField: PrimeField,
1007        G::Config: SWCurveConfig,
1008    {
1009        let mut rng = ark_std::test_rng();
1010
1011        let cs = ConstraintSystem::new_ref();
1012        let point_in = Projective::<G::Config>::zero();
1013        let point_out = Projective::<G::Config>::zero();
1014        let scalar = G::ScalarField::rand(&mut rng);
1015
1016        let point_in =
1017            ProjectiveVar::<G::Config, FpVar<G::BaseField>>::new_witness(cs.clone(), || {
1018                Ok(point_in)
1019            })?;
1020        let point_out =
1021            ProjectiveVar::<G::Config, FpVar<G::BaseField>>::new_input(cs.clone(), || {
1022                Ok(point_out)
1023            })?;
1024        let scalar = EmulatedFpVar::new_input(cs.clone(), || Ok(scalar))?;
1025
1026        let mul = point_in.scalar_mul_le(scalar.to_bits_le().unwrap().iter())?;
1027
1028        point_out.enforce_equal(&mul)?;
1029
1030        cs.is_satisfied()
1031    }
1032
1033    #[test]
1034    fn test_zero_point_scalar_mul() {
1035        assert!(zero_point_scalar_mul_satisfied::<ark_bls12_381::G1Projective>().unwrap());
1036        assert!(zero_point_scalar_mul_satisfied::<ark_pallas::Projective>().unwrap());
1037        assert!(zero_point_scalar_mul_satisfied::<ark_mnt4_298::G1Projective>().unwrap());
1038        assert!(zero_point_scalar_mul_satisfied::<ark_mnt6_298::G1Projective>().unwrap());
1039        assert!(zero_point_scalar_mul_satisfied::<ark_bn254::G1Projective>().unwrap());
1040    }
1041}