Skip to main content

soroban_sdk/crypto/
bls12_381.rs

1#[cfg(not(target_family = "wasm"))]
2use crate::xdr::ScVal;
3use crate::{
4    crypto::utils::BigInt,
5    env::internal::{self, BytesObject, U256Val, U64Val},
6    impl_bytesn_repr,
7    unwrap::{UnwrapInfallible, UnwrapOptimized},
8    Bytes, BytesN, ConversionError, Env, IntoVal, TryFromVal, Val, Vec, U256,
9};
10use core::{
11    cmp::Ordering,
12    fmt::Debug,
13    ops::{Add, Mul, Neg, Sub},
14};
15
16pub const FP_SERIALIZED_SIZE: usize = 48; // Size in bytes of a serialized Fp element in BLS12-381. The field modulus is 381 bits, requiring 48 bytes (384 bits) with 3 bits reserved for flags.
17pub const FP2_SERIALIZED_SIZE: usize = FP_SERIALIZED_SIZE * 2;
18pub const G1_SERIALIZED_SIZE: usize = FP_SERIALIZED_SIZE * 2; // Must match soroban_sdk_macro::map_type::G1_SERIALIZED_SIZE.
19pub const G2_SERIALIZED_SIZE: usize = FP2_SERIALIZED_SIZE * 2; // Must match soroban_sdk_macro::map_type::G2_SERIALIZED_SIZE.
20
21/// Bls12_381 provides access to curve and field arithmetics on the BLS12-381
22/// curve.
23pub struct Bls12_381 {
24    env: Env,
25}
26
27/// `Bls12381G1Affine` is a point in the G1 group (subgroup defined over the base field
28///  `Fq`) of the BLS12-381 elliptic curve
29///
30/// # Serialization:
31/// - The 96 bytes represent the **uncompressed encoding** of a point in G1. The
32///   Bytes consist of `be_byte(X) || be_byte(Y)`  (`||` is concatenation),
33///   where 'X' and 'Y' are the two coordinates, each being a base field element
34///   `Fp`
35/// - The most significant three bits (bits 0-3) of the first byte are reserved
36///   for encoding flags:
37///   - compression_flag (bit 0): Must always be unset (0), as only uncompressed
38///     points are supported.
39///   - infinity_flag (bit 1): Set if the point is the point at infinity (zero
40///     point), in which case all other bits must be zero.
41///   - sort_flag (bit 2): Must always be unset (0).
42///
43/// # Example Usage:
44/// ```rust
45/// use soroban_sdk::{Env, bytesn, crypto::bls12_381::{Bls12_381, Bls12381G1Affine}};
46/// let env = Env::default();
47/// let bls12_381 = env.crypto().bls12_381();
48/// let zero = Bls12381G1Affine::from_bytes(bytesn!(&env, 0x400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000));
49/// let one = Bls12381G1Affine::from_bytes(bytesn!(&env, 0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1));
50/// let res = bls12_381.g1_add(&zero, &one);
51/// assert_eq!(res, one);
52/// ```
53#[derive(Clone)]
54#[repr(transparent)]
55pub struct Bls12381G1Affine(BytesN<G1_SERIALIZED_SIZE>);
56
57/// Type alias for `Bls12381G1Affine` for convenience
58pub type G1Affine = Bls12381G1Affine;
59
60/// `Bls12381G2Affine` is a point in the G2 group (subgroup defined over the quadratic
61/// extension field `Fq2`) of the BLS12-381 elliptic curve
62///
63/// # Serialization:
64/// - The 192 bytes represent the **uncompressed encoding** of a point in G2.
65///   The bytes consist of `be_bytes(X_c1) || be_bytes(X_c0) || be_bytes(Y_c1)
66///   || be_bytes(Y_c0)` (`||` is concatenation), where 'X' and 'Y' are the two
67///   coordinates, each being an extension field element `Fp2` and `c0`, `c1`
68///   are components of `Fp2` (each being `Fp`).
69/// - The most significant three bits (bits 0-3) of the first byte are reserved
70///   for encoding flags:
71///   - compression_flag (bit 0): Must always be unset (0), as only uncompressed
72///     points are supported.
73///   - infinity_flag (bit 1): Set if the point is the point at infinity (zero
74///     point), in which case all other bits must be zero.
75///   - sort_flag (bit 2): Must always be unset (0).
76#[derive(Clone)]
77#[repr(transparent)]
78pub struct Bls12381G2Affine(BytesN<G2_SERIALIZED_SIZE>);
79
80/// Type alias for `Bls12381G2Affine` for convenience
81pub type G2Affine = Bls12381G2Affine;
82
83/// `Bls12381Fp` represents an element of the base field `Fq` of the BLS12-381 elliptic
84/// curve
85///
86/// # Serialization:
87/// - The 48 bytes represent the **big-endian encoding** of an element in the
88///   field `Fp`. The value is serialized as a big-endian integer.
89#[derive(Clone)]
90#[repr(transparent)]
91pub struct Bls12381Fp(BytesN<FP_SERIALIZED_SIZE>);
92
93/// Type alias for `Bls12381Fp` for convenience
94pub type Fp = Bls12381Fp;
95
96/// `Bls12381Fp2` represents an element of the quadratic extension field `Fq2` of the
97/// BLS12-381 elliptic curve
98///
99/// # Serialization:
100/// - The 96 bytes represent the **big-endian encoding** of an element in the
101///   field `Fp2`. The bytes consist of `be_bytes(c1) || be_bytes(c0)` (`||` is
102///   concatenation), where `c0` and `c1` are the two `Fp` elements (the real
103///   and imaginary components).
104#[derive(Clone)]
105#[repr(transparent)]
106pub struct Bls12381Fp2(BytesN<FP2_SERIALIZED_SIZE>);
107
108/// Type alias for `Bls12381Fp2` for convenience
109pub type Fp2 = Bls12381Fp2;
110
111/// `Fr` represents an element in the BLS12-381 scalar field, which is a prime
112/// field of order `r` (the order of the G1 and G2 groups). The struct is
113/// internally represented with an `U256`, all arithmetic operations follow
114/// modulo `r`.
115#[derive(Clone)]
116#[repr(transparent)]
117pub struct Fr(U256);
118
119impl_bytesn_repr!(Bls12381G1Affine, G1_SERIALIZED_SIZE);
120impl_bytesn_repr!(Bls12381G2Affine, G2_SERIALIZED_SIZE);
121impl_bytesn_repr!(Bls12381Fp, FP_SERIALIZED_SIZE);
122impl_bytesn_repr!(Bls12381Fp2, FP2_SERIALIZED_SIZE);
123
124impl Fp {
125    pub fn env(&self) -> &Env {
126        self.0.env()
127    }
128
129    // `Fp` represents an element in the base field of the BLS12-381 elliptic curve.
130    // For an element a ∈ Fp, its negation `-a` is defined as:
131    //   a + (-a) = 0 (mod p)
132    // where `p` is the field modulus, and to make a valid point coordinate on the
133    // curve, `a` also must be within the field range (i.e., 0 ≤ a < p).
134    fn checked_neg(&self) -> Option<Fp> {
135        let fp_bigint: BigInt<6> = (&self.0).into();
136        if fp_bigint.is_zero() {
137            return Some(self.clone());
138        }
139
140        // BLS12-381 base field modulus
141        const BLS12_381_MODULUS: [u64; 6] = [
142            13402431016077863595,
143            2210141511517208575,
144            7435674573564081700,
145            7239337960414712511,
146            5412103778470702295,
147            1873798617647539866,
148        ];
149        let mut res = BigInt(BLS12_381_MODULUS);
150
151        // Compute modulus - value
152        let borrow = res.sub_with_borrow(&fp_bigint);
153        if borrow {
154            return None;
155        }
156
157        let mut bytes = [0u8; FP_SERIALIZED_SIZE];
158        res.copy_into_array(&mut bytes);
159        Some(Fp::from_array(self.env(), &bytes))
160    }
161
162    /// Maps this `Fp` element to a `G1Affine` point using the [simplified SWU
163    /// mapping](https://www.rfc-editor.org/rfc/rfc9380.html#name-simplified-swu-for-ab-0).
164    ///
165    /// <div class="warning">
166    /// <h6>Warning</h6>
167    /// The resulting point is on the curve but may not be in the prime-order subgroup (operations
168    /// like pairing may fail). To ensure the point is in the prime-order subgroup, cofactor
169    /// clearing must be performed on the output.
170    ///
171    /// For applications requiring a point directly in the prime-order subgroup, consider using
172    /// `hash_to_g1`, which handles subgroup checks and cofactor clearing internally.
173    /// </div>
174    pub fn map_to_g1(&self) -> G1Affine {
175        self.env().crypto().bls12_381().map_fp_to_g1(self)
176    }
177}
178
179impl From<Fp> for BigInt<6> {
180    fn from(fp: Fp) -> Self {
181        let inner: Bytes = fp.0.into();
182        let mut limbs = [0u64; 6];
183        for i in 0..6u32 {
184            let start = i * 8;
185            let mut slice = [0u8; 8];
186            inner.slice(start..start + 8).copy_into_slice(&mut slice);
187            limbs[5 - i as usize] = u64::from_be_bytes(slice);
188        }
189        BigInt(limbs)
190    }
191}
192
193impl Neg for &Fp {
194    type Output = Fp;
195
196    fn neg(self) -> Self::Output {
197        match self.checked_neg() {
198            Some(v) => v,
199            None => sdk_panic!("invalid input - Fp is larger than the field modulus"),
200        }
201    }
202}
203
204impl Neg for Fp {
205    type Output = Fp;
206
207    fn neg(self) -> Self::Output {
208        (&self).neg()
209    }
210}
211
212impl G1Affine {
213    pub fn env(&self) -> &Env {
214        self.0.env()
215    }
216
217    pub fn is_in_subgroup(&self) -> bool {
218        self.env().crypto().bls12_381().g1_is_in_subgroup(self)
219    }
220
221    pub fn checked_add(&self, rhs: &Self) -> Option<Self> {
222        self.env().crypto().bls12_381().g1_checked_add(self, rhs)
223    }
224}
225
226impl Add for G1Affine {
227    type Output = G1Affine;
228
229    fn add(self, rhs: Self) -> Self::Output {
230        self.env().crypto().bls12_381().g1_add(&self, &rhs)
231    }
232}
233
234impl Mul<Fr> for G1Affine {
235    type Output = G1Affine;
236
237    fn mul(self, rhs: Fr) -> Self::Output {
238        self.env().crypto().bls12_381().g1_mul(&self, &rhs)
239    }
240}
241
242// G1Affine represents a point (X, Y) on the BLS12-381 curve where X, Y ∈ Fp
243// Negation of (X, Y) is defined as (X, -Y)
244impl Neg for &G1Affine {
245    type Output = G1Affine;
246
247    fn neg(self) -> Self::Output {
248        let mut inner: Bytes = (&self.0).into();
249        let y = Fp::try_from_val(
250            inner.env(),
251            inner.slice(FP_SERIALIZED_SIZE as u32..).as_val(),
252        )
253        .unwrap_optimized();
254        let neg_y = -y;
255        inner.copy_from_slice(FP_SERIALIZED_SIZE as u32, &neg_y.to_array());
256        G1Affine::from_bytes(BytesN::try_from_val(inner.env(), inner.as_val()).unwrap_optimized())
257    }
258}
259
260impl Neg for G1Affine {
261    type Output = G1Affine;
262
263    fn neg(self) -> Self::Output {
264        (&self).neg()
265    }
266}
267
268impl Fp2 {
269    pub fn env(&self) -> &Env {
270        self.0.env()
271    }
272
273    // An Fp2 element is represented as c0 + c1 * X, where:
274    // - c0, c1 are base field elements (Fp)
275    // - X is the quadratic non-residue used to construct the field extension
276    // The negation of c0 + c1 * X is (-c0) + (-c1) * X.
277    fn checked_neg(&self) -> Option<Fp2> {
278        let mut inner = self.to_array();
279        let mut slice0 = [0; FP_SERIALIZED_SIZE];
280        let mut slice1 = [0; FP_SERIALIZED_SIZE];
281        slice0.copy_from_slice(&inner[0..FP_SERIALIZED_SIZE]);
282        slice1.copy_from_slice(&inner[FP_SERIALIZED_SIZE..FP2_SERIALIZED_SIZE]);
283
284        // Convert both components to Fp and negate them
285        let c0 = Fp::from_array(self.env(), &slice0);
286        let c1 = Fp::from_array(self.env(), &slice1);
287
288        // If either component's negation fails, the whole operation fails
289        let neg_c0 = c0.checked_neg()?;
290        let neg_c1 = c1.checked_neg()?;
291
292        // Reconstruct the Fp2 element from negated components
293        inner[0..FP_SERIALIZED_SIZE].copy_from_slice(&neg_c0.to_array());
294        inner[FP_SERIALIZED_SIZE..FP2_SERIALIZED_SIZE].copy_from_slice(&neg_c1.to_array());
295
296        Some(Fp2::from_array(self.env(), &inner))
297    }
298
299    /// Maps this `Fp2` element to a `G2Affine` point using the [simplified SWU
300    /// mapping](https://www.rfc-editor.org/rfc/rfc9380.html#name-simplified-swu-for-ab-0).
301    ///
302    /// <div class="warning">
303    /// <h6>Warning</h6>
304    /// The resulting point is on the curve but may not be in the prime-order subgroup (operations
305    /// like pairing may fail). To ensure the point is in the prime-order subgroup, cofactor
306    /// clearing must be performed on the output.
307    ///
308    /// For applications requiring a point directly in the prime-order subgroup, consider using
309    /// `hash_to_g2`, which handles subgroup checks and cofactor clearing internally.
310    /// </div>
311    pub fn map_to_g2(&self) -> G2Affine {
312        self.env().crypto().bls12_381().map_fp2_to_g2(self)
313    }
314}
315
316impl Neg for &Fp2 {
317    type Output = Fp2;
318
319    fn neg(self) -> Self::Output {
320        match self.checked_neg() {
321            Some(v) => v,
322            None => sdk_panic!("invalid input - Fp2 component is larger than the field modulus"),
323        }
324    }
325}
326
327impl Neg for Fp2 {
328    type Output = Fp2;
329
330    fn neg(self) -> Self::Output {
331        (&self).neg()
332    }
333}
334
335impl G2Affine {
336    pub fn env(&self) -> &Env {
337        self.0.env()
338    }
339
340    pub fn is_in_subgroup(&self) -> bool {
341        self.env().crypto().bls12_381().g2_is_in_subgroup(self)
342    }
343
344    pub fn checked_add(&self, rhs: &Self) -> Option<Self> {
345        self.env().crypto().bls12_381().g2_checked_add(self, rhs)
346    }
347}
348
349impl Add for G2Affine {
350    type Output = G2Affine;
351
352    fn add(self, rhs: Self) -> Self::Output {
353        self.env().crypto().bls12_381().g2_add(&self, &rhs)
354    }
355}
356
357impl Mul<Fr> for G2Affine {
358    type Output = G2Affine;
359
360    fn mul(self, rhs: Fr) -> Self::Output {
361        self.env().crypto().bls12_381().g2_mul(&self, &rhs)
362    }
363}
364
365// G2Affine represents a point (X, Y) on the BLS12-381 quadratic extension curve where X, Y ∈ Fp2
366// Negation of (X, Y) is defined as (X, -Y)
367impl Neg for &G2Affine {
368    type Output = G2Affine;
369
370    fn neg(self) -> Self::Output {
371        let mut inner: Bytes = (&self.0).into();
372        let y = Fp2::try_from_val(
373            inner.env(),
374            inner.slice(FP2_SERIALIZED_SIZE as u32..).as_val(),
375        )
376        .unwrap_optimized();
377        let neg_y = -y;
378        inner.copy_from_slice(FP2_SERIALIZED_SIZE as u32, &neg_y.to_array());
379        G2Affine::from_bytes(BytesN::try_from_val(inner.env(), inner.as_val()).unwrap_optimized())
380    }
381}
382
383impl Neg for G2Affine {
384    type Output = G2Affine;
385
386    fn neg(self) -> Self::Output {
387        (&self).neg()
388    }
389}
390
391impl Fr {
392    pub fn env(&self) -> &Env {
393        self.0.env()
394    }
395
396    pub fn from_u256(value: U256) -> Self {
397        value.into()
398    }
399
400    pub fn to_u256(&self) -> U256 {
401        self.0.clone()
402    }
403
404    pub fn as_u256(&self) -> &U256 {
405        &self.0
406    }
407
408    pub fn from_bytes(bytes: BytesN<32>) -> Self {
409        U256::from_be_bytes(bytes.env(), bytes.as_ref()).into()
410    }
411
412    pub fn to_bytes(&self) -> BytesN<32> {
413        self.as_u256().to_be_bytes().try_into().unwrap_optimized()
414    }
415
416    pub fn as_val(&self) -> &Val {
417        self.0.as_val()
418    }
419
420    pub fn to_val(&self) -> Val {
421        self.0.to_val()
422    }
423
424    pub fn pow(&self, rhs: u64) -> Self {
425        self.env().crypto().bls12_381().fr_pow(self, rhs)
426    }
427
428    pub fn inv(&self) -> Self {
429        self.env().crypto().bls12_381().fr_inv(self)
430    }
431}
432
433impl From<U256> for Fr {
434    fn from(value: U256) -> Self {
435        Self(value)
436    }
437}
438
439impl From<&Fr> for U256Val {
440    fn from(value: &Fr) -> Self {
441        value.as_u256().into()
442    }
443}
444
445impl TryFromVal<Env, Val> for Fr {
446    type Error = ConversionError;
447
448    fn try_from_val(env: &Env, val: &Val) -> Result<Self, Self::Error> {
449        let u = U256::try_from_val(env, val)?;
450        Ok(Fr(u))
451    }
452}
453
454impl TryFromVal<Env, Fr> for Val {
455    type Error = ConversionError;
456
457    fn try_from_val(_env: &Env, fr: &Fr) -> Result<Self, Self::Error> {
458        Ok(fr.to_val())
459    }
460}
461
462impl TryFromVal<Env, &Fr> for Val {
463    type Error = ConversionError;
464
465    fn try_from_val(_env: &Env, fr: &&Fr) -> Result<Self, Self::Error> {
466        Ok(fr.to_val())
467    }
468}
469
470#[cfg(not(target_family = "wasm"))]
471impl From<&Fr> for ScVal {
472    fn from(v: &Fr) -> Self {
473        Self::from(&v.0)
474    }
475}
476
477#[cfg(not(target_family = "wasm"))]
478impl From<Fr> for ScVal {
479    fn from(v: Fr) -> Self {
480        (&v).into()
481    }
482}
483
484impl Eq for Fr {}
485
486impl PartialEq for Fr {
487    fn eq(&self, other: &Self) -> bool {
488        self.as_u256().partial_cmp(other.as_u256()) == Some(Ordering::Equal)
489    }
490}
491
492impl Debug for Fr {
493    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
494        write!(f, "Fr({:?})", self.as_u256())
495    }
496}
497
498impl Add for Fr {
499    type Output = Fr;
500
501    fn add(self, rhs: Self) -> Self::Output {
502        self.env().crypto().bls12_381().fr_add(&self, &rhs)
503    }
504}
505
506impl Sub for Fr {
507    type Output = Fr;
508
509    fn sub(self, rhs: Self) -> Self::Output {
510        self.env().crypto().bls12_381().fr_sub(&self, &rhs)
511    }
512}
513
514impl Mul for Fr {
515    type Output = Fr;
516
517    fn mul(self, rhs: Self) -> Self::Output {
518        self.env().crypto().bls12_381().fr_mul(&self, &rhs)
519    }
520}
521
522impl Bls12_381 {
523    pub(crate) fn new(env: &Env) -> Bls12_381 {
524        Bls12_381 { env: env.clone() }
525    }
526
527    pub fn env(&self) -> &Env {
528        &self.env
529    }
530
531    // g1
532
533    /// Checks if a point `p` in G1 is in the correct subgroup.
534    pub fn g1_is_in_subgroup(&self, p: &G1Affine) -> bool {
535        let env = self.env();
536        let res = internal::Env::bls12_381_check_g1_is_in_subgroup(env, p.to_object())
537            .unwrap_infallible();
538        res.into()
539    }
540
541    /// Adds two points `p0` and `p1` in G1.
542    pub fn g1_add(&self, p0: &G1Affine, p1: &G1Affine) -> G1Affine {
543        let env = self.env();
544        let bin = internal::Env::bls12_381_g1_add(env, p0.to_object(), p1.to_object())
545            .unwrap_infallible();
546        unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
547    }
548
549    /// Adds two points `p0` and `p1` in G1, ensuring that the result is in the
550    /// correct subgroup. Note the subgroup check is computationally expensive,
551    /// so if want to perform a series of additions i.e. `agg = p0 + p1 + .. + pn`,
552    /// it may make sense to only call g1_checked_add on the final addition,
553    /// while using `g1_add` (non-checked version) on the intermediate ones.
554    pub fn g1_checked_add(&self, p0: &G1Affine, p1: &G1Affine) -> Option<G1Affine> {
555        let env = self.env();
556        let bin = internal::Env::bls12_381_g1_add(env, p0.to_object(), p1.to_object())
557            .unwrap_infallible();
558        let res = unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) };
559        let is_in_correct_subgroup: bool =
560            internal::Env::bls12_381_check_g1_is_in_subgroup(env, res.to_object())
561                .unwrap_optimized()
562                .into();
563        match is_in_correct_subgroup {
564            true => Some(res),
565            false => None,
566        }
567    }
568
569    /// Multiplies a point `p0` in G1 by a scalar.
570    pub fn g1_mul(&self, p0: &G1Affine, scalar: &Fr) -> G1Affine {
571        let env = self.env();
572        let bin =
573            internal::Env::bls12_381_g1_mul(env, p0.to_object(), scalar.into()).unwrap_infallible();
574        unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
575    }
576
577    /// Performs a multi-scalar multiplication (MSM) operation in G1.
578    pub fn g1_msm(&self, vp: Vec<G1Affine>, vs: Vec<Fr>) -> G1Affine {
579        let env = self.env();
580        let bin = internal::Env::bls12_381_g1_msm(env, vp.into(), vs.into()).unwrap_infallible();
581        unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
582    }
583
584    /// Maps an element in the base field `Fp` to a point in G1.
585    pub fn map_fp_to_g1(&self, fp: &Fp) -> G1Affine {
586        let env = self.env();
587        let bin = internal::Env::bls12_381_map_fp_to_g1(env, fp.to_object()).unwrap_infallible();
588        unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
589    }
590
591    /// Hashes a message `msg` to a point in G1, using a domain separation tag `dst`.
592    pub fn hash_to_g1(&self, msg: &Bytes, dst: &Bytes) -> G1Affine {
593        let env = self.env();
594        let bin = internal::Env::bls12_381_hash_to_g1(env, msg.into(), dst.to_object())
595            .unwrap_infallible();
596        unsafe { G1Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
597    }
598
599    // g2
600
601    /// Checks if a point `p` in G2 is in the correct subgroup.
602    pub fn g2_is_in_subgroup(&self, p: &G2Affine) -> bool {
603        let env = self.env();
604        let res = internal::Env::bls12_381_check_g2_is_in_subgroup(env, p.to_object())
605            .unwrap_infallible();
606        res.into()
607    }
608
609    /// Adds two points `p0` and `p1` in G2.
610    pub fn g2_add(&self, p0: &G2Affine, p1: &G2Affine) -> G2Affine {
611        let env = self.env();
612        let bin = internal::Env::bls12_381_g2_add(env, p0.to_object(), p1.to_object())
613            .unwrap_infallible();
614        unsafe { G2Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
615    }
616
617    /// Adds two points `p0` and `p1` in G2, ensuring that the result is in the
618    /// correct subgroup. Note the subgroup check is computationally expensive,
619    /// so if want to perform a series of additions i.e. `agg = p0 + p1 + .. +pn`,     
620    /// it may make sense to only call g2_checked_add on the final addition,
621    /// while using `g2_add` (non-checked version) on the intermediate ones.
622    pub fn g2_checked_add(&self, p0: &G2Affine, p1: &G2Affine) -> Option<G2Affine> {
623        let env = self.env();
624        let bin = internal::Env::bls12_381_g2_add(env, p0.to_object(), p1.to_object())
625            .unwrap_infallible();
626        let res = unsafe { G2Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) };
627        let is_in_correct_subgroup: bool =
628            internal::Env::bls12_381_check_g2_is_in_subgroup(env, res.to_object())
629                .unwrap_optimized()
630                .into();
631        match is_in_correct_subgroup {
632            true => Some(res),
633            false => None,
634        }
635    }
636
637    /// Multiplies a point `p0` in G2 by a scalar.
638    pub fn g2_mul(&self, p0: &G2Affine, scalar: &Fr) -> G2Affine {
639        let env = self.env();
640        let bin =
641            internal::Env::bls12_381_g2_mul(env, p0.to_object(), scalar.into()).unwrap_infallible();
642        unsafe { G2Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
643    }
644
645    /// Performs a multi-scalar multiplication (MSM) operation in G2.
646    pub fn g2_msm(&self, vp: Vec<G2Affine>, vs: Vec<Fr>) -> G2Affine {
647        let env = self.env();
648        let bin = internal::Env::bls12_381_g2_msm(env, vp.into(), vs.into()).unwrap_infallible();
649        unsafe { G2Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
650    }
651
652    /// Maps an element in the base field `Fp2` to a point in G2.
653    pub fn map_fp2_to_g2(&self, fp2: &Fp2) -> G2Affine {
654        let env = self.env();
655        let bin = internal::Env::bls12_381_map_fp2_to_g2(env, fp2.to_object()).unwrap_infallible();
656        unsafe { G2Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
657    }
658
659    /// Hashes a message `msg` to a point in G2, using a domain separation tag `dst`.
660    pub fn hash_to_g2(&self, msg: &Bytes, dst: &Bytes) -> G2Affine {
661        let env = self.env();
662        let bin = internal::Env::bls12_381_hash_to_g2(env, msg.into(), dst.to_object())
663            .unwrap_infallible();
664        unsafe { G2Affine::from_bytes(BytesN::unchecked_new(env.clone(), bin)) }
665    }
666
667    // pairing
668
669    /// Performs a pairing check between vectors of points in G1 and G2.
670    ///
671    /// This function computes the pairing for each pair of points in the
672    /// provided vectors `vp1` (G1 points) and `vp2` (G2 points) and verifies if
673    /// the overall pairing result is equal to the identity in the target group.
674    ///
675    /// # Returns:
676    /// - `true` if the pairing check holds (i.e., the pairing result is valid
677    ///   and equal to the identity element), otherwise `false`.
678    ///
679    /// # Panics:
680    /// - If the lengths of `vp1` and `vp2` are not equal or if they are empty.
681    pub fn pairing_check(&self, vp1: Vec<G1Affine>, vp2: Vec<G2Affine>) -> bool {
682        let env = self.env();
683        internal::Env::bls12_381_multi_pairing_check(env, vp1.into(), vp2.into())
684            .unwrap_infallible()
685            .into()
686    }
687
688    // scalar arithmetic
689
690    /// Adds two scalars in the BLS12-381 scalar field `Fr`.
691    pub fn fr_add(&self, lhs: &Fr, rhs: &Fr) -> Fr {
692        let env = self.env();
693        let v = internal::Env::bls12_381_fr_add(env, lhs.into(), rhs.into()).unwrap_infallible();
694        U256::try_from_val(env, &v).unwrap_infallible().into()
695    }
696
697    /// Subtracts one scalar from another in the BLS12-381 scalar field `Fr`.
698    pub fn fr_sub(&self, lhs: &Fr, rhs: &Fr) -> Fr {
699        let env = self.env();
700        let v = internal::Env::bls12_381_fr_sub(env, lhs.into(), rhs.into()).unwrap_infallible();
701        U256::try_from_val(env, &v).unwrap_infallible().into()
702    }
703
704    /// Multiplies two scalars in the BLS12-381 scalar field `Fr`.
705    pub fn fr_mul(&self, lhs: &Fr, rhs: &Fr) -> Fr {
706        let env = self.env();
707        let v = internal::Env::bls12_381_fr_mul(env, lhs.into(), rhs.into()).unwrap_infallible();
708        U256::try_from_val(env, &v).unwrap_infallible().into()
709    }
710
711    /// Raises a scalar to the power of a given exponent in the BLS12-381 scalar field `Fr`.
712    pub fn fr_pow(&self, lhs: &Fr, rhs: u64) -> Fr {
713        let env = self.env();
714        let rhs = U64Val::try_from_val(env, &rhs).unwrap_optimized();
715        let v = internal::Env::bls12_381_fr_pow(env, lhs.into(), rhs).unwrap_infallible();
716        U256::try_from_val(env, &v).unwrap_infallible().into()
717    }
718
719    /// Computes the multiplicative inverse of a scalar in the BLS12-381 scalar field `Fr`.
720    pub fn fr_inv(&self, lhs: &Fr) -> Fr {
721        let env = self.env();
722        let v = internal::Env::bls12_381_fr_inv(env, lhs.into()).unwrap_infallible();
723        U256::try_from_val(env, &v).unwrap_infallible().into()
724    }
725}
726
727#[cfg(test)]
728mod test {
729    use super::*;
730
731    #[test]
732    fn test_g1affine_to_val() {
733        let env = Env::default();
734
735        let g1 = G1Affine::from_bytes(BytesN::from_array(&env, &[1; 96]));
736        let val: Val = g1.clone().into_val(&env);
737        let rt: G1Affine = val.into_val(&env);
738
739        assert_eq!(g1, rt);
740    }
741
742    #[test]
743    fn test_ref_g1affine_to_val() {
744        let env = Env::default();
745
746        let g1 = G1Affine::from_bytes(BytesN::from_array(&env, &[1; 96]));
747        let val: Val = (&g1).into_val(&env);
748        let rt: G1Affine = val.into_val(&env);
749
750        assert_eq!(g1, rt);
751    }
752
753    #[test]
754    fn test_doule_ref_g1affine_to_val() {
755        let env = Env::default();
756
757        let g1 = G1Affine::from_bytes(BytesN::from_array(&env, &[1; 96]));
758        let val: Val = (&&g1).into_val(&env);
759        let rt: G1Affine = val.into_val(&env);
760
761        assert_eq!(g1, rt);
762    }
763
764    #[test]
765    fn test_fr_to_val() {
766        let env = Env::default();
767
768        let fr = Fr::from_bytes(BytesN::from_array(&env, &[1; 32]));
769        let val: Val = fr.clone().into_val(&env);
770        let rt: Fr = val.into_val(&env);
771
772        assert_eq!(fr, rt);
773    }
774
775    #[test]
776    fn test_ref_fr_to_val() {
777        let env = Env::default();
778
779        let fr = Fr::from_bytes(BytesN::from_array(&env, &[1; 32]));
780        let val: Val = (&fr).into_val(&env);
781        let rt: Fr = val.into_val(&env);
782
783        assert_eq!(fr, rt);
784    }
785
786    #[test]
787    fn test_double_ref_fr_to_val() {
788        let env = Env::default();
789
790        let fr = Fr::from_bytes(BytesN::from_array(&env, &[1; 32]));
791        let val: Val = (&&fr).into_val(&env);
792        let rt: Fr = val.into_val(&env);
793
794        assert_eq!(fr, rt);
795    }
796}