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<>Element> for GTElement {
65 fn mul_assign(&mut self, rhs: >Element) {
66 unsafe {
67 blst_fp12_mul(&mut self.0, &self.0, &rhs.0);
68 }
69 }
70}
71
72impl Mul<>Element> for >Element {
73 type Output = GTElement;
74 fn mul(self, rhs: >Element) -> 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}