nam_blstrs/
g1.rs

1//! An implementation of the $\mathbb{G}_1$ group of BLS12-381.
2
3use core::{
4    borrow::Borrow,
5    fmt,
6    iter::Sum,
7    ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
8};
9use std::io::Read;
10
11use blst::*;
12use group::{
13    Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup,
14    prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup},
15};
16use rand_core::RngCore;
17use subtle::{Choice, ConditionallySelectable, CtOption};
18
19use crate::{Bls12, Engine, G2Affine, Gt, PairingCurveAffine, Scalar, fp::Fp};
20
21/// This is an element of $\mathbb{G}_1$ represented in the affine coordinate space.
22/// It is ideal to keep elements in this representation to reduce memory usage and
23/// improve performance through the use of mixed curve model arithmetic.
24#[derive(Copy, Clone)]
25#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
26#[repr(transparent)]
27pub struct G1Affine(pub(crate) blst_p1_affine);
28
29const COMPRESSED_SIZE: usize = 48;
30const UNCOMPRESSED_SIZE: usize = 96;
31
32impl fmt::Debug for G1Affine {
33    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
34        let is_ident: bool = self.is_identity().into();
35        f.debug_struct("G1Affine")
36            .field("x", &self.x())
37            .field("y", &self.y())
38            .field("infinity", &is_ident)
39            .finish()
40    }
41}
42
43impl fmt::Display for G1Affine {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        if self.is_identity().into() {
46            write!(f, "G1Affine(Infinity)")
47        } else {
48            write!(f, "G1Affine(x={}, y={})", self.x(), self.y())
49        }
50    }
51}
52
53impl Default for G1Affine {
54    fn default() -> G1Affine {
55        G1Affine::identity()
56    }
57}
58
59impl From<&G1Projective> for G1Affine {
60    fn from(p: &G1Projective) -> G1Affine {
61        let mut out = blst_p1_affine::default();
62
63        unsafe { blst_p1_to_affine(&mut out, &p.0) };
64
65        G1Affine(out)
66    }
67}
68
69impl From<G1Projective> for G1Affine {
70    fn from(p: G1Projective) -> G1Affine {
71        G1Affine::from(&p)
72    }
73}
74
75impl AsRef<blst_p1_affine> for G1Affine {
76    fn as_ref(&self) -> &blst_p1_affine {
77        &self.0
78    }
79}
80
81impl AsMut<blst_p1_affine> for G1Affine {
82    fn as_mut(&mut self) -> &mut blst_p1_affine {
83        &mut self.0
84    }
85}
86
87impl Eq for G1Affine {}
88impl PartialEq for G1Affine {
89    #[inline]
90    fn eq(&self, other: &Self) -> bool {
91        unsafe { blst_p1_affine_is_equal(&self.0, &other.0) }
92    }
93}
94
95impl Neg for &G1Projective {
96    type Output = G1Projective;
97
98    #[inline]
99    fn neg(self) -> G1Projective {
100        -*self
101    }
102}
103
104impl Neg for G1Projective {
105    type Output = G1Projective;
106
107    #[inline]
108    fn neg(mut self) -> G1Projective {
109        unsafe { blst_p1_cneg(&mut self.0, true) };
110        self
111    }
112}
113
114impl Neg for &G1Affine {
115    type Output = G1Affine;
116
117    #[inline]
118    fn neg(self) -> G1Affine {
119        -*self
120    }
121}
122
123impl Neg for G1Affine {
124    type Output = G1Affine;
125
126    #[inline]
127    fn neg(mut self) -> G1Affine {
128        // Missing for affine in blst
129        if (!self.is_identity()).into() {
130            unsafe {
131                blst_fp_cneg(&mut self.0.y, &self.0.y, true);
132            }
133        }
134        self
135    }
136}
137
138impl Add<&G1Projective> for &G1Projective {
139    type Output = G1Projective;
140
141    #[inline]
142    fn add(self, rhs: &G1Projective) -> G1Projective {
143        let mut out = blst_p1::default();
144        unsafe { blst_p1_add_or_double(&mut out, &self.0, &rhs.0) };
145        G1Projective(out)
146    }
147}
148
149impl Add<&G1Projective> for &G1Affine {
150    type Output = G1Projective;
151
152    #[inline]
153    fn add(self, rhs: &G1Projective) -> G1Projective {
154        rhs.add_mixed(self)
155    }
156}
157
158impl Add<&G1Affine> for &G1Projective {
159    type Output = G1Projective;
160
161    #[inline]
162    fn add(self, rhs: &G1Affine) -> G1Projective {
163        self.add_mixed(rhs)
164    }
165}
166
167impl Sub<&G1Projective> for &G1Projective {
168    type Output = G1Projective;
169
170    #[inline]
171    fn sub(self, rhs: &G1Projective) -> G1Projective {
172        self + (-rhs)
173    }
174}
175
176impl Sub<&G1Projective> for &G1Affine {
177    type Output = G1Projective;
178
179    #[inline]
180    fn sub(self, rhs: &G1Projective) -> G1Projective {
181        self + (-rhs)
182    }
183}
184
185impl Sub<&G1Affine> for &G1Projective {
186    type Output = G1Projective;
187
188    #[inline]
189    fn sub(self, rhs: &G1Affine) -> G1Projective {
190        self + (-rhs)
191    }
192}
193
194impl AddAssign<&G1Projective> for G1Projective {
195    #[inline]
196    fn add_assign(&mut self, rhs: &G1Projective) {
197        unsafe { blst_p1_add_or_double(&mut self.0, &self.0, &rhs.0) };
198    }
199}
200
201impl SubAssign<&G1Projective> for G1Projective {
202    #[inline]
203    fn sub_assign(&mut self, rhs: &G1Projective) {
204        *self += &-rhs;
205    }
206}
207
208impl AddAssign<&G1Affine> for G1Projective {
209    #[inline]
210    fn add_assign(&mut self, rhs: &G1Affine) {
211        unsafe { blst_p1_add_or_double_affine(&mut self.0, &self.0, &rhs.0) };
212    }
213}
214
215impl SubAssign<&G1Affine> for G1Projective {
216    #[inline]
217    fn sub_assign(&mut self, rhs: &G1Affine) {
218        *self += &-rhs;
219    }
220}
221
222impl Mul<&Scalar> for &G1Projective {
223    type Output = G1Projective;
224
225    fn mul(self, scalar: &Scalar) -> Self::Output {
226        self.multiply(scalar)
227    }
228}
229
230impl Mul<&Scalar> for &G1Affine {
231    type Output = G1Projective;
232
233    fn mul(self, scalar: &Scalar) -> Self::Output {
234        G1Projective::from(self).multiply(scalar)
235    }
236}
237
238impl MulAssign<&Scalar> for G1Projective {
239    #[inline]
240    fn mul_assign(&mut self, rhs: &Scalar) {
241        *self = *self * rhs;
242    }
243}
244
245impl MulAssign<&Scalar> for G1Affine {
246    #[inline]
247    fn mul_assign(&mut self, rhs: &Scalar) {
248        *self = (*self * rhs).into();
249    }
250}
251
252impl_add_sub!(G1Projective);
253impl_add_sub!(G1Projective, G1Affine);
254impl_add_sub!(G1Affine, G1Projective, G1Projective);
255
256impl_add_sub_assign!(G1Projective);
257impl_add_sub_assign!(G1Projective, G1Affine);
258
259impl_mul!(G1Projective, Scalar);
260impl_mul!(G1Affine, Scalar, G1Projective);
261
262impl_mul_assign!(G1Projective, Scalar);
263impl_mul_assign!(G1Affine, Scalar);
264
265impl<T> Sum<T> for G1Projective
266where
267    T: Borrow<G1Projective>,
268{
269    fn sum<I>(iter: I) -> Self
270    where
271        I: Iterator<Item = T>,
272    {
273        iter.fold(Self::identity(), |acc, item| acc + item.borrow())
274    }
275}
276
277impl ConditionallySelectable for G1Affine {
278    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
279        G1Affine(blst_p1_affine {
280            x: Fp::conditional_select(&a.x(), &b.x(), choice).0,
281            y: Fp::conditional_select(&a.y(), &b.y(), choice).0,
282        })
283    }
284}
285
286impl ConditionallySelectable for G1Projective {
287    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
288        G1Projective(blst_p1 {
289            x: Fp::conditional_select(&a.x(), &b.x(), choice).0,
290            y: Fp::conditional_select(&a.y(), &b.y(), choice).0,
291            z: Fp::conditional_select(&a.z(), &b.z(), choice).0,
292        })
293    }
294}
295
296impl G1Affine {
297    /// Serializes this element into compressed form.
298    pub fn to_compressed(&self) -> [u8; COMPRESSED_SIZE] {
299        let mut out = [0u8; COMPRESSED_SIZE];
300
301        unsafe {
302            blst_p1_affine_compress(out.as_mut_ptr(), &self.0);
303        }
304
305        out
306    }
307
308    /// Serializes this element into uncompressed form.
309    pub fn to_uncompressed(&self) -> [u8; UNCOMPRESSED_SIZE] {
310        let mut out = [0u8; UNCOMPRESSED_SIZE];
311
312        unsafe {
313            blst_p1_affine_serialize(out.as_mut_ptr(), &self.0);
314        }
315
316        out
317    }
318
319    /// Attempts to deserialize an uncompressed element.
320    pub fn from_uncompressed(bytes: &[u8; UNCOMPRESSED_SIZE]) -> CtOption<Self> {
321        G1Affine::from_uncompressed_unchecked(bytes)
322            .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free()))
323    }
324
325    /// Attempts to deserialize an uncompressed element, not checking if the
326    /// element is on the curve and not checking if it is in the correct subgroup.
327    ///
328    /// **This is dangerous to call unless you trust the bytes you are reading; otherwise,
329    /// API invariants may be broken.** Please consider using `from_uncompressed()` instead.
330    pub fn from_uncompressed_unchecked(bytes: &[u8; UNCOMPRESSED_SIZE]) -> CtOption<Self> {
331        let mut raw = blst_p1_affine::default();
332        let success =
333            unsafe { blst_p1_deserialize(&mut raw, bytes.as_ptr()) == BLST_ERROR::BLST_SUCCESS };
334        CtOption::new(G1Affine(raw), Choice::from(success as u8))
335    }
336
337    /// Attempts to deserialize a compressed element.
338    pub fn from_compressed(bytes: &[u8; COMPRESSED_SIZE]) -> CtOption<Self> {
339        G1Affine::from_compressed_unchecked(bytes)
340            .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free()))
341    }
342
343    /// Attempts to deserialize an uncompressed element, not checking if the
344    /// element is in the correct subgroup.
345    ///
346    /// **This is dangerous to call unless you trust the bytes you are reading; otherwise,
347    /// API invariants may be broken.** Please consider using `from_compressed()` instead.
348    pub fn from_compressed_unchecked(bytes: &[u8; COMPRESSED_SIZE]) -> CtOption<Self> {
349        let mut raw = blst_p1_affine::default();
350        let success =
351            unsafe { blst_p1_uncompress(&mut raw, bytes.as_ptr()) == BLST_ERROR::BLST_SUCCESS };
352        CtOption::new(G1Affine(raw), Choice::from(success as u8))
353    }
354
355    /// Returns true if this point is free of an $h$-torsion component, and so it
356    /// exists within the $q$-order subgroup $\mathbb{G}_1$. This should always return true
357    /// unless an "unchecked" API was used.
358    pub fn is_torsion_free(&self) -> Choice {
359        unsafe { Choice::from(blst_p1_affine_in_g1(&self.0) as u8) }
360    }
361
362    /// Returns true if this point is on the curve. This should always return
363    /// true unless an "unchecked" API was used.
364    pub fn is_on_curve(&self) -> Choice {
365        unsafe { Choice::from(blst_p1_affine_on_curve(&self.0) as u8) }
366    }
367
368    pub fn from_raw_unchecked(x: Fp, y: Fp, _infinity: bool) -> Self {
369        // FIXME: what about infinity?
370        let raw = blst_p1_affine { x: x.0, y: y.0 };
371
372        G1Affine(raw)
373    }
374
375    /// Returns the x coordinate.
376    pub fn x(&self) -> Fp {
377        Fp(self.0.x)
378    }
379
380    /// Returns the y coordinate.
381    pub fn y(&self) -> Fp {
382        Fp(self.0.y)
383    }
384
385    pub const fn uncompressed_size() -> usize {
386        UNCOMPRESSED_SIZE
387    }
388
389    pub const fn compressed_size() -> usize {
390        COMPRESSED_SIZE
391    }
392
393    #[inline]
394    pub fn raw_fmt_size() -> usize {
395        let s = G1Affine::uncompressed_size();
396        s + 1
397    }
398
399    pub fn write_raw<W: std::io::Write>(&self, mut writer: W) -> Result<usize, std::io::Error> {
400        if self.is_identity().into() {
401            writer.write_all(&[1])?;
402        } else {
403            writer.write_all(&[0])?;
404        }
405        let raw = self.to_uncompressed();
406        writer.write_all(&raw)?;
407
408        Ok(Self::raw_fmt_size())
409    }
410
411    pub fn read_raw<R: Read>(mut reader: R) -> Result<Self, std::io::Error> {
412        let mut buf = [0u8];
413        reader.read_exact(&mut buf)?;
414        let _infinity = buf[0] == 1;
415
416        let mut buf = [0u8; UNCOMPRESSED_SIZE];
417        reader.read_exact(&mut buf)?;
418        let res = Self::from_uncompressed_unchecked(&buf);
419        if res.is_some().into() {
420            Ok(res.unwrap())
421        } else {
422            Err(std::io::Error::new(
423                std::io::ErrorKind::InvalidData,
424                "not on curve",
425            ))
426        }
427    }
428
429    pub fn read_raw_checked<R: Read>(mut reader: R) -> Result<Self, std::io::Error> {
430        let mut buf = [0u8];
431        reader.read_exact(&mut buf)?;
432        let _infinity = buf[0] == 1;
433
434        let mut buf = [0u8; UNCOMPRESSED_SIZE];
435        reader.read_exact(&mut buf)?;
436        let res = Self::from_uncompressed(&buf);
437        if res.is_some().into() {
438            Ok(res.unwrap())
439        } else {
440            Err(std::io::Error::new(
441                std::io::ErrorKind::InvalidData,
442                "not on curve",
443            ))
444        }
445    }
446}
447
448/// This is an element of $\mathbb{G}_1$ represented in the projective coordinate space.
449#[derive(Copy, Clone)]
450#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
451#[repr(transparent)]
452pub struct G1Projective(pub(crate) blst_p1);
453
454impl fmt::Debug for G1Projective {
455    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
456        f.debug_struct("G1Projective")
457            .field("x", &self.x())
458            .field("y", &self.y())
459            .field("z", &self.z())
460            .finish()
461    }
462}
463
464impl fmt::Display for G1Projective {
465    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
466        write!(f, "{}", G1Affine::from(self))
467    }
468}
469
470impl AsRef<blst_p1> for G1Projective {
471    fn as_ref(&self) -> &blst_p1 {
472        &self.0
473    }
474}
475
476impl AsMut<blst_p1> for G1Projective {
477    fn as_mut(&mut self) -> &mut blst_p1 {
478        &mut self.0
479    }
480}
481
482impl From<&G1Affine> for G1Projective {
483    fn from(p: &G1Affine) -> G1Projective {
484        let mut out = blst_p1::default();
485
486        unsafe { blst_p1_from_affine(&mut out, &p.0) };
487
488        G1Projective(out)
489    }
490}
491
492impl From<G1Affine> for G1Projective {
493    fn from(p: G1Affine) -> G1Projective {
494        G1Projective::from(&p)
495    }
496}
497
498impl Eq for G1Projective {}
499impl PartialEq for G1Projective {
500    #[inline]
501    fn eq(&self, other: &Self) -> bool {
502        let self_is_zero: bool = self.is_identity().into();
503        let other_is_zero: bool = other.is_identity().into();
504        (self_is_zero && other_is_zero)
505            || (!self_is_zero && !other_is_zero && unsafe { blst_p1_is_equal(&self.0, &other.0) })
506    }
507}
508
509impl G1Projective {
510    /// Serializes this element into compressed form.
511    pub fn to_compressed(&self) -> [u8; COMPRESSED_SIZE] {
512        let mut out = [0u8; COMPRESSED_SIZE];
513
514        unsafe {
515            blst_p1_compress(out.as_mut_ptr(), &self.0);
516        }
517
518        out
519    }
520
521    /// Serializes this element into uncompressed form.
522    pub fn to_uncompressed(&self) -> [u8; UNCOMPRESSED_SIZE] {
523        let mut out = [0u8; UNCOMPRESSED_SIZE];
524
525        unsafe {
526            blst_p1_serialize(out.as_mut_ptr(), &self.0);
527        }
528
529        out
530    }
531
532    /// Attempts to deserialize an uncompressed element.
533    pub fn from_uncompressed(bytes: &[u8; UNCOMPRESSED_SIZE]) -> CtOption<Self> {
534        G1Affine::from_uncompressed(bytes).map(Into::into)
535    }
536
537    /// Attempts to deserialize an uncompressed element, not checking if the
538    /// element is on the curve and not checking if it is in the correct subgroup.
539    ///
540    /// **This is dangerous to call unless you trust the bytes you are reading; otherwise,
541    /// API invariants may be broken.** Please consider using `from_uncompressed()` instead.
542    pub fn from_uncompressed_unchecked(bytes: &[u8; UNCOMPRESSED_SIZE]) -> CtOption<Self> {
543        G1Affine::from_uncompressed_unchecked(bytes).map(Into::into)
544    }
545
546    /// Attempts to deserialize a compressed element.
547    pub fn from_compressed(bytes: &[u8; COMPRESSED_SIZE]) -> CtOption<Self> {
548        G1Affine::from_compressed(bytes).map(Into::into)
549    }
550
551    /// Attempts to deserialize an uncompressed element, not checking if the
552    /// element is in the correct subgroup.
553    ///
554    /// **This is dangerous to call unless you trust the bytes you are reading; otherwise,
555    /// API invariants may be broken.** Please consider using `from_compressed()` instead.
556    pub fn from_compressed_unchecked(bytes: &[u8; COMPRESSED_SIZE]) -> CtOption<Self> {
557        G1Affine::from_compressed_unchecked(bytes).map(Into::into)
558    }
559
560    /// Adds this point to another point in the affine model.
561    fn add_mixed(&self, rhs: &G1Affine) -> G1Projective {
562        let mut out = blst_p1::default();
563
564        unsafe { blst_p1_add_or_double_affine(&mut out, &self.0, &rhs.0) };
565
566        G1Projective(out)
567    }
568
569    /// Returns true if this point is on the curve. This should always return
570    /// true unless an "unchecked" API was used.
571    pub fn is_on_curve(&self) -> Choice {
572        let is_on_curve = unsafe { Choice::from(blst_p1_on_curve(&self.0) as u8) };
573        is_on_curve | self.is_identity()
574    }
575
576    fn multiply(&self, scalar: &Scalar) -> G1Projective {
577        let mut out = blst_p1::default();
578
579        // Scalar is 255 bits wide.
580        const NBITS: usize = 255;
581
582        unsafe { blst_p1_mult(&mut out, &self.0, scalar.to_bytes_le().as_ptr(), NBITS) };
583
584        G1Projective(out)
585    }
586
587    pub fn from_raw_unchecked(x: Fp, y: Fp, z: Fp) -> Self {
588        let raw = blst_p1 {
589            x: x.0,
590            y: y.0,
591            z: z.0,
592        };
593
594        G1Projective(raw)
595    }
596
597    /// Returns the x coordinate.
598    pub fn x(&self) -> Fp {
599        Fp(self.0.x)
600    }
601
602    /// Returns the y coordinate.
603    pub fn y(&self) -> Fp {
604        Fp(self.0.y)
605    }
606
607    /// Returns the z coordinate.
608    pub fn z(&self) -> Fp {
609        Fp(self.0.z)
610    }
611
612    /// Hash to curve algorithm.
613    pub fn hash_to_curve(msg: &[u8], dst: &[u8], aug: &[u8]) -> Self {
614        let mut res = Self::identity();
615        unsafe {
616            blst_hash_to_g1(
617                &mut res.0,
618                msg.as_ptr(),
619                msg.len(),
620                dst.as_ptr(),
621                dst.len(),
622                aug.as_ptr(),
623                aug.len(),
624            );
625        }
626        res
627    }
628
629    /// Perform a multi-exponentiation, aka "multi-scalar-multiplication" (MSM) using `blst`'s implementation of Pippenger's algorithm.
630    /// Note: `scalars` is cloned in this method.
631    pub fn multi_exp(points: &[Self], scalars: &[Scalar]) -> Self {
632        let n = if points.len() < scalars.len() {
633            points.len()
634        } else {
635            scalars.len()
636        };
637        let points =
638            unsafe { std::slice::from_raw_parts(points.as_ptr() as *const blst_p1, points.len()) };
639
640        let points = p1_affines::from(points);
641
642        let mut scalar_bytes: Vec<u8> = Vec::with_capacity(n * 32);
643        for a in scalars.iter().map(|s| s.to_bytes_le()) {
644            scalar_bytes.extend_from_slice(&a);
645        }
646
647        let res = points.mult(scalar_bytes.as_slice(), 255);
648
649        G1Projective(res)
650    }
651}
652
653impl Group for G1Projective {
654    type Scalar = Scalar;
655
656    fn random(mut rng: impl RngCore) -> Self {
657        let mut out = blst_p1::default();
658        let mut msg = [0u8; 64];
659        rng.fill_bytes(&mut msg);
660        const DST: [u8; 16] = [0; 16];
661        const AUG: [u8; 16] = [0; 16];
662
663        unsafe {
664            blst_encode_to_g1(
665                &mut out,
666                msg.as_ptr(),
667                msg.len(),
668                DST.as_ptr(),
669                DST.len(),
670                AUG.as_ptr(),
671                AUG.len(),
672            )
673        };
674
675        G1Projective(out)
676    }
677
678    fn identity() -> Self {
679        G1Projective(blst_p1::default())
680    }
681
682    fn generator() -> Self {
683        G1Projective(unsafe { *blst_p1_generator() })
684    }
685
686    fn is_identity(&self) -> Choice {
687        unsafe { Choice::from(blst_p1_is_inf(&self.0) as u8) }
688    }
689
690    fn double(&self) -> Self {
691        let mut double = blst_p1::default();
692        unsafe { blst_p1_double(&mut double, &self.0) };
693        G1Projective(double)
694    }
695}
696
697impl WnafGroup for G1Projective {
698    fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize {
699        const RECOMMENDATIONS: [usize; 12] =
700            [1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569];
701
702        let mut ret = 4;
703        for r in &RECOMMENDATIONS {
704            if num_scalars > *r {
705                ret += 1;
706            } else {
707                break;
708            }
709        }
710
711        ret
712    }
713}
714
715impl PrimeGroup for G1Projective {}
716
717impl Curve for G1Projective {
718    type AffineRepr = G1Affine;
719
720    fn to_affine(&self) -> Self::AffineRepr {
721        self.into()
722    }
723}
724
725impl PrimeCurve for G1Projective {
726    type Affine = G1Affine;
727}
728
729impl PrimeCurveAffine for G1Affine {
730    type Scalar = Scalar;
731    type Curve = G1Projective;
732
733    fn identity() -> Self {
734        G1Affine(blst_p1_affine::default())
735    }
736
737    fn generator() -> Self {
738        G1Affine(unsafe { *blst_p1_affine_generator() })
739    }
740
741    fn is_identity(&self) -> Choice {
742        unsafe { Choice::from(blst_p1_affine_is_inf(&self.0) as u8) }
743    }
744
745    fn to_curve(&self) -> Self::Curve {
746        self.into()
747    }
748}
749
750impl GroupEncoding for G1Projective {
751    type Repr = G1Compressed;
752
753    fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
754        Self::from_compressed(&bytes.0)
755    }
756
757    fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
758        Self::from_compressed_unchecked(&bytes.0)
759    }
760
761    fn to_bytes(&self) -> Self::Repr {
762        G1Compressed(self.to_compressed())
763    }
764}
765
766impl GroupEncoding for G1Affine {
767    type Repr = G1Compressed;
768
769    fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
770        Self::from_compressed(&bytes.0)
771    }
772
773    fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
774        Self::from_compressed_unchecked(&bytes.0)
775    }
776
777    fn to_bytes(&self) -> Self::Repr {
778        G1Compressed(self.to_compressed())
779    }
780}
781
782impl UncompressedEncoding for G1Affine {
783    type Uncompressed = G1Uncompressed;
784
785    fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption<Self> {
786        Self::from_uncompressed(&bytes.0)
787    }
788
789    fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption<Self> {
790        Self::from_uncompressed_unchecked(&bytes.0)
791    }
792
793    fn to_uncompressed(&self) -> Self::Uncompressed {
794        G1Uncompressed(self.to_uncompressed())
795    }
796}
797
798#[derive(Copy, Clone)]
799#[repr(transparent)]
800pub struct G1Uncompressed([u8; UNCOMPRESSED_SIZE]);
801
802encoded_point_delegations!(G1Uncompressed);
803
804impl Default for G1Uncompressed {
805    fn default() -> Self {
806        G1Uncompressed([0u8; UNCOMPRESSED_SIZE])
807    }
808}
809
810impl fmt::Debug for G1Uncompressed {
811    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
812        self.0[..].fmt(formatter)
813    }
814}
815
816#[derive(Copy, Clone)]
817#[repr(transparent)]
818pub struct G1Compressed([u8; COMPRESSED_SIZE]);
819
820encoded_point_delegations!(G1Compressed);
821
822impl Default for G1Compressed {
823    fn default() -> Self {
824        G1Compressed([0u8; COMPRESSED_SIZE])
825    }
826}
827
828impl fmt::Debug for G1Compressed {
829    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
830        self.0[..].fmt(formatter)
831    }
832}
833
834impl PairingCurveAffine for G1Affine {
835    type Pair = G2Affine;
836    type PairingResult = Gt;
837
838    fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult {
839        <Bls12 as Engine>::pairing(self, other)
840    }
841}
842
843#[cfg(feature = "gpu")]
844impl ec_gpu::GpuName for G1Affine {
845    fn name() -> String {
846        ec_gpu::name!()
847    }
848}
849
850#[cfg(test)]
851mod tests {
852    #![allow(clippy::eq_op)]
853
854    use super::*;
855
856    use ff::Field;
857    use rand_core::SeedableRng;
858    use rand_xorshift::XorShiftRng;
859
860    #[test]
861    fn curve_tests() {
862        let mut rng = XorShiftRng::from_seed([
863            0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
864            0xbc, 0xe5,
865        ]);
866
867        {
868            let z = G1Projective::identity();
869            assert_eq!(z.is_identity().unwrap_u8(), 1);
870        }
871
872        // Negation edge case with zero.
873        {
874            let mut z = G1Projective::identity();
875            z = z.neg();
876            assert_eq!(z.is_identity().unwrap_u8(), 1);
877        }
878
879        // Doubling edge case with zero.
880        {
881            let mut z = G1Projective::identity();
882            z = z.double();
883            assert_eq!(z.is_identity().unwrap_u8(), 1);
884        }
885
886        // Addition edge cases with zero
887        {
888            let mut r = G1Projective::random(&mut rng);
889            let rcopy = r;
890            r += &G1Projective::identity();
891            assert_eq!(r, rcopy);
892            r += &G1Affine::identity();
893            assert_eq!(r, rcopy);
894
895            let mut z = G1Projective::identity();
896            z += &G1Projective::identity();
897            assert_eq!(z.is_identity().unwrap_u8(), 1);
898            z += &G1Affine::identity();
899            assert_eq!(z.is_identity().unwrap_u8(), 1);
900
901            let mut z2 = z;
902            z2 += &r;
903
904            z += &G1Affine::from(r);
905
906            assert_eq!(z, z2);
907            assert_eq!(z, r);
908        }
909
910        // Transformations
911        {
912            let a = G1Projective::random(&mut rng);
913            let b: G1Projective = G1Affine::from(a).into();
914            let c = G1Projective::from(G1Affine::from(G1Projective::from(G1Affine::from(a))));
915
916            assert_eq!(a, b);
917            assert_eq!(b, c);
918        }
919    }
920
921    #[test]
922    fn test_is_on_curve() {
923        assert_eq!(G1Projective::identity().is_on_curve().unwrap_u8(), 1);
924        assert_eq!(G1Projective::generator().is_on_curve().unwrap_u8(), 1);
925
926        assert_eq!(G1Affine::identity().is_on_curve().unwrap_u8(), 1);
927        assert_eq!(G1Affine::generator().is_on_curve().unwrap_u8(), 1);
928
929        let z = Fp::from_raw_unchecked([
930            0xba7afa1f9a6fe250,
931            0xfa0f5b595eafe731,
932            0x3bdc477694c306e7,
933            0x2149be4b3949fa24,
934            0x64aa6e0649b2078c,
935            0x12b108ac33643c3e,
936        ]);
937
938        let generator = G1Affine::generator();
939        let z2 = z.square();
940        let mut test =
941            G1Projective::from_raw_unchecked(generator.x() * z2, generator.y() * (z2 * z), z);
942
943        assert_eq!(test.is_on_curve().unwrap_u8(), 1);
944
945        test.0.x = z.0;
946        assert_eq!(test.is_on_curve().unwrap_u8(), 0);
947    }
948
949    #[test]
950    fn test_affine_point_equality() {
951        let a = G1Affine::generator();
952        let b = G1Affine::identity();
953
954        assert_eq!(a, a);
955        assert_eq!(b, b);
956        assert_ne!(a, b);
957        assert_ne!(b, a);
958    }
959
960    #[test]
961    fn test_projective_point_equality() {
962        let a = G1Projective::generator();
963        let b = G1Projective::identity();
964
965        assert_eq!(a, a);
966        assert_eq!(b, b);
967        assert_ne!(a, b);
968        assert_ne!(b, a);
969
970        let z = Fp::from_raw_unchecked([
971            0xba7afa1f9a6fe250,
972            0xfa0f5b595eafe731,
973            0x3bdc477694c306e7,
974            0x2149be4b3949fa24,
975            0x64aa6e0649b2078c,
976            0x12b108ac33643c3e,
977        ]);
978
979        let z2 = z.square();
980        let mut c = G1Projective::from_raw_unchecked(a.x() * z2, a.y() * (z2 * z), z);
981        assert_eq!(c.is_on_curve().unwrap_u8(), 1);
982
983        assert_eq!(a, c);
984        assert_eq!(c, a);
985        assert_ne!(b, c);
986        assert_ne!(c, b);
987
988        c.0.y = (-c.y()).0;
989        assert_eq!(c.is_on_curve().unwrap_u8(), 1);
990
991        assert_ne!(a, c);
992        assert_ne!(b, c);
993        assert_ne!(c, a);
994        assert_ne!(c, b);
995
996        c.0.y = (-c.y()).0;
997        c.0.x = z.0;
998        assert_eq!(c.is_on_curve().unwrap_u8(), 0);
999        assert_ne!(a, b);
1000        assert_ne!(a, c);
1001        assert_ne!(b, c);
1002    }
1003
1004    #[test]
1005    fn test_projective_to_affine() {
1006        let a = G1Projective::generator();
1007        let b = G1Projective::identity();
1008
1009        assert_eq!(G1Affine::from(a).is_on_curve().unwrap_u8(), 1);
1010        assert_eq!(G1Affine::from(a).is_identity().unwrap_u8(), 0);
1011        assert_eq!(G1Affine::from(b).is_on_curve().unwrap_u8(), 1);
1012        assert_eq!(G1Affine::from(b).is_identity().unwrap_u8(), 1);
1013
1014        let z = Fp::from_raw_unchecked([
1015            0xba7afa1f9a6fe250,
1016            0xfa0f5b595eafe731,
1017            0x3bdc477694c306e7,
1018            0x2149be4b3949fa24,
1019            0x64aa6e0649b2078c,
1020            0x12b108ac33643c3e,
1021        ]);
1022
1023        let z2 = z.square();
1024        let c = G1Projective::from_raw_unchecked(a.x() * z2, a.y() * (z2 * z), z);
1025
1026        assert_eq!(G1Affine::from(c), G1Affine::generator());
1027    }
1028
1029    #[test]
1030    fn test_affine_to_projective() {
1031        let a = G1Affine::generator();
1032        let b = G1Affine::identity();
1033
1034        assert_eq!(G1Projective::from(a).is_on_curve().unwrap_u8(), 1);
1035        assert_eq!(G1Projective::from(a).is_identity().unwrap_u8(), 0);
1036        assert_eq!(G1Projective::from(b).is_on_curve().unwrap_u8(), 1);
1037        assert_eq!(G1Projective::from(b).is_identity().unwrap_u8(), 1);
1038    }
1039
1040    #[test]
1041    fn test_doubling() {
1042        {
1043            let tmp = G1Projective::identity().double();
1044            assert_eq!(tmp.is_identity().unwrap_u8(), 1);
1045            assert_eq!(tmp.is_on_curve().unwrap_u8(), 1);
1046        }
1047        {
1048            let tmp = G1Projective::generator().double();
1049            assert_eq!(tmp.is_identity().unwrap_u8(), 0);
1050            assert_eq!(tmp.is_on_curve().unwrap_u8(), 1);
1051
1052            assert_eq!(
1053                G1Affine::from(tmp),
1054                G1Affine::from_raw_unchecked(
1055                    Fp::from_raw_unchecked([
1056                        0x53e978ce58a9ba3c,
1057                        0x3ea0583c4f3d65f9,
1058                        0x4d20bb47f0012960,
1059                        0xa54c664ae5b2b5d9,
1060                        0x26b552a39d7eb21f,
1061                        0x8895d26e68785
1062                    ]),
1063                    Fp::from_raw_unchecked([
1064                        0x70110b3298293940,
1065                        0xda33c5393f1f6afc,
1066                        0xb86edfd16a5aa785,
1067                        0xaec6d1c9e7b1c895,
1068                        0x25cfc2b522d11720,
1069                        0x6361c83f8d09b15
1070                    ]),
1071                    false
1072                )
1073            );
1074        }
1075    }
1076
1077    #[test]
1078    fn test_projective_addition() {
1079        {
1080            let a = G1Projective::identity();
1081            let b = G1Projective::identity();
1082            let c = a + b;
1083            assert_eq!(c.is_identity().unwrap_u8(), 1);
1084            assert_eq!(c.is_on_curve().unwrap_u8(), 1);
1085        }
1086        {
1087            let a = G1Projective::identity();
1088            let mut b = G1Projective::generator();
1089            {
1090                let z = Fp::from_raw_unchecked([
1091                    0xba7afa1f9a6fe250,
1092                    0xfa0f5b595eafe731,
1093                    0x3bdc477694c306e7,
1094                    0x2149be4b3949fa24,
1095                    0x64aa6e0649b2078c,
1096                    0x12b108ac33643c3e,
1097                ]);
1098
1099                let z2 = z.square();
1100                b = G1Projective::from_raw_unchecked(b.x() * (z2), b.y() * (z2 * z), z);
1101            }
1102            let c = a + b;
1103            assert_eq!(c.is_identity().unwrap_u8(), 0);
1104            assert_eq!(c.is_on_curve().unwrap_u8(), 1);
1105            assert_eq!(c, G1Projective::generator());
1106        }
1107        {
1108            let a = G1Projective::identity();
1109            let mut b = G1Projective::generator();
1110            {
1111                let z = Fp::from_raw_unchecked([
1112                    0xba7afa1f9a6fe250,
1113                    0xfa0f5b595eafe731,
1114                    0x3bdc477694c306e7,
1115                    0x2149be4b3949fa24,
1116                    0x64aa6e0649b2078c,
1117                    0x12b108ac33643c3e,
1118                ]);
1119
1120                let z2 = z.square();
1121                b = G1Projective::from_raw_unchecked(b.x() * (z2), b.y() * (z2 * z), z);
1122            }
1123            let c = b + a;
1124            assert_eq!(c.is_identity().unwrap_u8(), 0);
1125            assert_eq!(c.is_on_curve().unwrap_u8(), 1);
1126            assert_eq!(c, G1Projective::generator());
1127        }
1128        {
1129            let a = G1Projective::generator().double().double(); // 4P
1130            let b = G1Projective::generator().double(); // 2P
1131            let c = a + b;
1132
1133            let mut d = G1Projective::generator();
1134            for _ in 0..5 {
1135                d += G1Projective::generator();
1136            }
1137            assert_eq!(c.is_identity().unwrap_u8(), 0);
1138            assert_eq!(c.is_on_curve().unwrap_u8(), 1);
1139            assert_eq!(d.is_identity().unwrap_u8(), 0);
1140            assert_eq!(d.is_on_curve().unwrap_u8(), 1);
1141            assert_eq!(c, d);
1142        }
1143
1144        // Degenerate case
1145        {
1146            let mut beta = Fp::from_raw_unchecked([
1147                0xcd03c9e48671f071,
1148                0x5dab22461fcda5d2,
1149                0x587042afd3851b95,
1150                0x8eb60ebe01bacb9e,
1151                0x3f97d6e83d050d2,
1152                0x18f0206554638741,
1153            ]);
1154            beta = beta.square();
1155            let a = G1Projective::generator().double().double();
1156            let b = G1Projective::from_raw_unchecked(a.x() * beta, -a.y(), a.z());
1157            assert_eq!(a.is_on_curve().unwrap_u8(), 1);
1158            assert_eq!(b.is_on_curve().unwrap_u8(), 1);
1159
1160            let c = a + b;
1161            assert_eq!(
1162                G1Affine::from(c),
1163                G1Affine::from(G1Projective::from_raw_unchecked(
1164                    Fp::from_raw_unchecked([
1165                        0x29e1e987ef68f2d0,
1166                        0xc5f3ec531db03233,
1167                        0xacd6c4b6ca19730f,
1168                        0x18ad9e827bc2bab7,
1169                        0x46e3b2c5785cc7a9,
1170                        0x7e571d42d22ddd6
1171                    ]),
1172                    Fp::from_raw_unchecked([
1173                        0x94d117a7e5a539e7,
1174                        0x8e17ef673d4b5d22,
1175                        0x9d746aaf508a33ea,
1176                        0x8c6d883d2516c9a2,
1177                        0xbc3b8d5fb0447f7,
1178                        0x7bfa4c7210f4f44
1179                    ]),
1180                    Fp::ONE,
1181                ))
1182            );
1183            assert_eq!(c.is_identity().unwrap_u8(), 0);
1184            assert_eq!(c.is_on_curve().unwrap_u8(), 1);
1185        }
1186    }
1187
1188    #[test]
1189    fn test_mixed_addition() {
1190        {
1191            let a = G1Affine::identity();
1192            let b = G1Projective::identity();
1193            let c = a + b;
1194            assert_eq!(c.is_identity().unwrap_u8(), 1);
1195            assert_eq!(c.is_on_curve().unwrap_u8(), 1);
1196        }
1197        {
1198            let a = G1Affine::identity();
1199            let mut b = G1Projective::generator();
1200            {
1201                let z = Fp::from_raw_unchecked([
1202                    0xba7afa1f9a6fe250,
1203                    0xfa0f5b595eafe731,
1204                    0x3bdc477694c306e7,
1205                    0x2149be4b3949fa24,
1206                    0x64aa6e0649b2078c,
1207                    0x12b108ac33643c3e,
1208                ]);
1209
1210                let z2 = z.square();
1211                b = G1Projective::from_raw_unchecked(b.x() * (z2), b.y() * (z2 * z), z);
1212            }
1213            let c = a + b;
1214            assert_eq!(c.is_identity().unwrap_u8(), 0);
1215            assert_eq!(c.is_on_curve().unwrap_u8(), 1);
1216            assert_eq!(c, G1Projective::generator());
1217        }
1218        {
1219            let a = G1Affine::identity();
1220            let mut b = G1Projective::generator();
1221            {
1222                let z = Fp::from_raw_unchecked([
1223                    0xba7afa1f9a6fe250,
1224                    0xfa0f5b595eafe731,
1225                    0x3bdc477694c306e7,
1226                    0x2149be4b3949fa24,
1227                    0x64aa6e0649b2078c,
1228                    0x12b108ac33643c3e,
1229                ]);
1230
1231                let z2 = z.square();
1232                b = G1Projective::from_raw_unchecked(b.x() * (z2), b.y() * (z2 * z), z);
1233            }
1234            let c = b + a;
1235            assert_eq!(c.is_identity().unwrap_u8(), 0);
1236            assert_eq!(c.is_on_curve().unwrap_u8(), 1);
1237            assert_eq!(c, G1Projective::generator());
1238        }
1239        {
1240            let a = G1Projective::generator().double().double(); // 4P
1241            let b = G1Projective::generator().double(); // 2P
1242            let c = a + b;
1243
1244            let mut d = G1Projective::generator();
1245            for _ in 0..5 {
1246                d += G1Affine::generator();
1247            }
1248            assert_eq!(c.is_identity().unwrap_u8(), 0);
1249            assert_eq!(c.is_on_curve().unwrap_u8(), 1);
1250            assert_eq!(d.is_identity().unwrap_u8(), 0);
1251            assert_eq!(d.is_on_curve().unwrap_u8(), 1);
1252            assert_eq!(c, d);
1253        }
1254
1255        // Degenerate case
1256        {
1257            let mut beta = Fp::from_raw_unchecked([
1258                0xcd03c9e48671f071,
1259                0x5dab22461fcda5d2,
1260                0x587042afd3851b95,
1261                0x8eb60ebe01bacb9e,
1262                0x3f97d6e83d050d2,
1263                0x18f0206554638741,
1264            ]);
1265            beta = beta.square();
1266            let a = G1Projective::generator().double().double();
1267            let b = G1Projective::from_raw_unchecked(a.x() * beta, -a.y(), a.z());
1268            let a = G1Affine::from(a);
1269            assert_eq!(a.is_on_curve().unwrap_u8(), 1);
1270            assert_eq!(b.is_on_curve().unwrap_u8(), 1);
1271
1272            let c = a + b;
1273            assert_eq!(
1274                G1Affine::from(c),
1275                G1Affine::from(G1Projective::from_raw_unchecked(
1276                    Fp::from_raw_unchecked([
1277                        0x29e1e987ef68f2d0,
1278                        0xc5f3ec531db03233,
1279                        0xacd6c4b6ca19730f,
1280                        0x18ad9e827bc2bab7,
1281                        0x46e3b2c5785cc7a9,
1282                        0x7e571d42d22ddd6
1283                    ]),
1284                    Fp::from_raw_unchecked([
1285                        0x94d117a7e5a539e7,
1286                        0x8e17ef673d4b5d22,
1287                        0x9d746aaf508a33ea,
1288                        0x8c6d883d2516c9a2,
1289                        0xbc3b8d5fb0447f7,
1290                        0x7bfa4c7210f4f44
1291                    ]),
1292                    Fp::ONE
1293                ))
1294            );
1295            assert_eq!(c.is_identity().unwrap_u8(), 0);
1296            assert_eq!(c.is_on_curve().unwrap_u8(), 1);
1297        }
1298    }
1299
1300    #[test]
1301    fn test_projective_negation_and_subtraction() {
1302        let a = G1Projective::generator().double();
1303        assert_eq!(a + (-a), G1Projective::identity());
1304        assert_eq!(a + (-a), a - a);
1305    }
1306
1307    #[test]
1308    fn test_affine_projective_negation_and_subtraction() {
1309        let a = G1Affine::generator();
1310        assert_eq!(G1Projective::from(a) + (-a), G1Projective::identity());
1311        assert_eq!(G1Projective::from(a) + (-a), G1Projective::from(a) - a);
1312    }
1313
1314    #[test]
1315    fn test_projective_scalar_multiplication() {
1316        let g = G1Projective::generator();
1317        let a = Scalar(blst::blst_fr {
1318            l: [
1319                0x2b568297a56da71c,
1320                0xd8c39ecb0ef375d1,
1321                0x435c38da67bfbf96,
1322                0x8088a05026b659b2,
1323            ],
1324        });
1325        let b = Scalar(blst_fr {
1326            l: [
1327                0x785fdd9b26ef8b85,
1328                0xc997f25837695c18,
1329                0x4c8dbc39e7b756c1,
1330                0x70d9b6cc6d87df20,
1331            ],
1332        });
1333        let c = a * b;
1334
1335        assert_eq!((g * a) * b, g * c);
1336    }
1337
1338    #[test]
1339    fn test_affine_scalar_multiplication() {
1340        let g = G1Affine::generator();
1341        let a = Scalar(blst::blst_fr {
1342            l: [
1343                0x2b568297a56da71c,
1344                0xd8c39ecb0ef375d1,
1345                0x435c38da67bfbf96,
1346                0x8088a05026b659b2,
1347            ],
1348        });
1349        let b = Scalar(blst::blst_fr {
1350            l: [
1351                0x785fdd9b26ef8b85,
1352                0xc997f25837695c18,
1353                0x4c8dbc39e7b756c1,
1354                0x70d9b6cc6d87df20,
1355            ],
1356        });
1357        let c = a * b;
1358
1359        assert_eq!(G1Affine::from(g * a) * b, g * c);
1360    }
1361
1362    #[test]
1363    fn g1_curve_tests() {
1364        use group::tests::curve_tests;
1365        curve_tests::<G1Projective>();
1366    }
1367
1368    #[test]
1369    fn test_g1_is_identity() {
1370        assert_eq!(G1Projective::identity().is_identity().unwrap_u8(), 1);
1371        assert_eq!(G1Projective::generator().is_identity().unwrap_u8(), 0);
1372        assert_eq!(G1Affine::identity().is_identity().unwrap_u8(), 1);
1373        assert_eq!(G1Affine::generator().is_identity().unwrap_u8(), 0);
1374    }
1375
1376    #[test]
1377    fn test_g1_serialization_roundtrip() {
1378        let mut rng = XorShiftRng::from_seed([
1379            0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
1380            0xbc, 0xe5,
1381        ]);
1382
1383        for _ in 0..100 {
1384            let el: G1Affine = G1Projective::random(&mut rng).into();
1385            let c = el.to_compressed();
1386            assert_eq!(G1Affine::from_compressed(&c).unwrap(), el);
1387            assert_eq!(G1Affine::from_compressed_unchecked(&c).unwrap(), el);
1388
1389            let u = el.to_uncompressed();
1390            assert_eq!(G1Affine::from_uncompressed(&u).unwrap(), el);
1391            assert_eq!(G1Affine::from_uncompressed_unchecked(&u).unwrap(), el);
1392
1393            let c = el.to_bytes();
1394            assert_eq!(G1Affine::from_bytes(&c).unwrap(), el);
1395            assert_eq!(G1Affine::from_bytes_unchecked(&c).unwrap(), el);
1396
1397            let el = G1Projective::random(&mut rng);
1398            let c = el.to_compressed();
1399            assert_eq!(G1Projective::from_compressed(&c).unwrap(), el);
1400            assert_eq!(G1Projective::from_compressed_unchecked(&c).unwrap(), el);
1401
1402            let u = el.to_uncompressed();
1403            assert_eq!(G1Projective::from_uncompressed(&u).unwrap(), el);
1404            assert_eq!(G1Projective::from_uncompressed_unchecked(&u).unwrap(), el);
1405
1406            let c = el.to_bytes();
1407            assert_eq!(G1Projective::from_bytes(&c).unwrap(), el);
1408            assert_eq!(G1Projective::from_bytes_unchecked(&c).unwrap(), el);
1409        }
1410    }
1411
1412    #[test]
1413    fn test_multi_exp() {
1414        const SIZE: usize = 10;
1415        let mut rng = XorShiftRng::from_seed([
1416            0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
1417            0xbc, 0xe5,
1418        ]);
1419
1420        let points: Vec<G1Projective> = (0..SIZE).map(|_| G1Projective::random(&mut rng)).collect();
1421        let scalars: Vec<Scalar> = (0..SIZE).map(|_| Scalar::random(&mut rng)).collect();
1422
1423        let mut naive = points[0] * scalars[0];
1424        for i in 1..SIZE {
1425            naive += points[i] * scalars[i];
1426        }
1427
1428        let pippenger = G1Projective::multi_exp(points.as_slice(), scalars.as_slice());
1429
1430        assert_eq!(naive, pippenger);
1431    }
1432}