chik_bls/
gtelement.rs

1use blst::*;
2use chik_sha2::Sha256;
3use chik_traits::chik_error::Result;
4use chik_traits::{read_bytes, Streamable};
5#[cfg(feature = "py-bindings")]
6use pyo3::exceptions::PyNotImplementedError;
7#[cfg(feature = "py-bindings")]
8use pyo3::prelude::*;
9#[cfg(feature = "py-bindings")]
10use pyo3::types::PyType;
11use std::fmt;
12use std::hash::{Hash, Hasher};
13use std::io::Cursor;
14use std::mem::MaybeUninit;
15use std::ops::{Mul, MulAssign};
16
17#[cfg_attr(
18    feature = "py-bindings",
19    pyo3::pyclass,
20    derive(chik_py_streamable_macro::PyStreamable)
21)]
22#[derive(Clone)]
23pub struct GTElement(pub(crate) blst_fp12);
24
25impl GTElement {
26    const SIZE: usize = std::mem::size_of::<blst_fp12>();
27
28    pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> Self {
29        let gt = unsafe {
30            let mut gt = MaybeUninit::<blst_fp12>::uninit();
31            std::ptr::copy_nonoverlapping(bytes.as_ptr(), gt.as_mut_ptr().cast::<u8>(), Self::SIZE);
32            gt.assume_init()
33        };
34        Self(gt)
35    }
36
37    pub fn to_bytes(&self) -> [u8; Self::SIZE] {
38        unsafe {
39            let mut bytes = MaybeUninit::<[u8; Self::SIZE]>::uninit();
40            let buf: *const blst_fp12 = &self.0;
41            std::ptr::copy_nonoverlapping(
42                buf.cast::<u8>(),
43                bytes.as_mut_ptr().cast::<u8>(),
44                Self::SIZE,
45            );
46            bytes.assume_init()
47        }
48    }
49}
50
51impl PartialEq for GTElement {
52    fn eq(&self, other: &Self) -> bool {
53        unsafe { blst_fp12_is_equal(&self.0, &other.0) }
54    }
55}
56impl Eq for GTElement {}
57
58impl Hash for GTElement {
59    fn hash<H: Hasher>(&self, state: &mut H) {
60        state.write(&self.to_bytes());
61    }
62}
63
64impl MulAssign<&GTElement> for GTElement {
65    fn mul_assign(&mut self, rhs: &GTElement) {
66        unsafe {
67            blst_fp12_mul(&mut self.0, &self.0, &rhs.0);
68        }
69    }
70}
71
72impl Mul<&GTElement> for &GTElement {
73    type Output = GTElement;
74    fn mul(self, rhs: &GTElement) -> GTElement {
75        let gt = unsafe {
76            let mut gt = MaybeUninit::<blst_fp12>::uninit();
77            blst_fp12_mul(gt.as_mut_ptr(), &self.0, &rhs.0);
78            gt.assume_init()
79        };
80        GTElement(gt)
81    }
82}
83
84impl fmt::Debug for GTElement {
85    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
86        formatter.write_fmt(format_args!(
87            "<GTElement {}>",
88            &hex::encode(self.to_bytes())
89        ))
90    }
91}
92
93impl Streamable for GTElement {
94    fn update_digest(&self, digest: &mut Sha256) {
95        digest.update(self.to_bytes());
96    }
97
98    fn stream(&self, out: &mut Vec<u8>) -> Result<()> {
99        out.extend_from_slice(&self.to_bytes());
100        Ok(())
101    }
102
103    fn parse<const TRUSTED: bool>(input: &mut Cursor<&[u8]>) -> Result<Self> {
104        Ok(GTElement::from_bytes(
105            read_bytes(input, Self::SIZE)?.try_into().unwrap(),
106        ))
107    }
108}
109
110#[cfg(feature = "py-bindings")]
111#[pyo3::pymethods]
112impl GTElement {
113    #[classattr]
114    #[pyo3(name = "SIZE")]
115    pub const PY_SIZE: usize = Self::SIZE;
116
117    pub fn __str__(&self) -> String {
118        hex::encode(self.to_bytes())
119    }
120
121    #[classmethod]
122    #[pyo3(name = "from_parent")]
123    pub fn from_parent(_cls: &Bound<'_, PyType>, _instance: &Self) -> PyResult<PyObject> {
124        Err(PyNotImplementedError::new_err(
125            "GTElement does not support from_parent().",
126        ))
127    }
128
129    #[must_use]
130    pub fn __mul__(&self, rhs: &Self) -> Self {
131        let mut ret = self.clone();
132        ret *= rhs;
133        ret
134    }
135
136    pub fn __imul__(&mut self, rhs: &Self) {
137        *self *= rhs;
138    }
139}
140
141#[cfg(feature = "py-bindings")]
142mod pybindings {
143    use super::*;
144
145    use crate::parse_hex::parse_hex_string;
146    use chik_traits::{FromJsonDict, ToJsonDict};
147
148    impl ToJsonDict for GTElement {
149        fn to_json_dict(&self, py: Python<'_>) -> PyResult<PyObject> {
150            let bytes = self.to_bytes();
151            Ok(("0x".to_string() + &hex::encode(bytes))
152                .into_pyobject(py)?
153                .into_any()
154                .unbind())
155        }
156    }
157
158    impl FromJsonDict for GTElement {
159        fn from_json_dict(o: &Bound<'_, PyAny>) -> PyResult<Self> {
160            Ok(Self::from_bytes(
161                parse_hex_string(o, Self::SIZE, "GTElement")?
162                    .as_slice()
163                    .try_into()
164                    .unwrap(),
165            ))
166        }
167    }
168}