use crate::{
backend::{self, BackendXOnly, TimeSensitive},
hash::HashInto,
marker::*,
Point, Scalar,
};
use rand_core::{CryptoRng, RngCore};
#[derive(Clone, Copy, Eq, Hash)]
pub struct XOnly(pub(crate) backend::XOnly);
impl XOnly {
pub fn from_bytes(bytes: [u8; 32]) -> Option<Self> {
backend::XOnly::from_bytes(bytes).map(Self::from_inner)
}
pub fn from_slice(slice: &[u8]) -> Option<Self> {
if slice.len() != 32 {
return None;
}
let mut bytes = [0u8; 32];
bytes.copy_from_slice(slice);
Self::from_bytes(bytes)
}
pub(crate) fn from_inner(inner: backend::XOnly) -> Self {
XOnly(inner)
}
pub fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
let mut bytes = [0u8; 32];
rng.fill_bytes(&mut bytes);
Self::from_bytes(bytes).unwrap_or_else(|| Self::random(rng))
}
pub fn as_bytes(&self) -> &[u8; 32] {
self.0.as_bytes()
}
pub fn into_bytes(self) -> [u8; 32] {
self.0.into_bytes()
}
}
impl XOnly {
pub fn to_point(&self) -> Point<EvenY, Public, NonZero> {
Point::from_inner(BackendXOnly::into_norm_point_even_y(self.0.clone()), EvenY)
}
pub fn from_scalar_mul<GT>(G: &Point<GT>, x: &mut Scalar<impl Secrecy>) -> Self {
let X = crate::op::scalar_mul_point(x, G).mark::<Normal>();
let needs_negation = !X.is_y_even();
x.conditional_negate(needs_negation);
X.to_xonly()
}
}
impl HashInto for XOnly {
fn hash_into(&self, hash: &mut impl digest::Digest) {
hash.update(self.as_bytes())
}
}
impl PartialEq<XOnly> for XOnly {
fn eq(&self, rhs: &XOnly) -> bool {
crate::backend::ConstantTime::xonly_eq(&self.0, &rhs.0)
}
}
impl<T, Z, S> PartialEq<XOnly> for Point<T, S, Z> {
fn eq(&self, rhs: &XOnly) -> bool {
crate::op::PointEqXOnly::point_eq_xonly(self, rhs)
}
}
impl From<XOnly> for Point<EvenY, Public, NonZero> {
fn from(xonly: XOnly) -> Self {
Point::from_inner(BackendXOnly::into_norm_point_even_y(xonly.0), EvenY)
}
}
impl<T, Z, S> PartialEq<Point<T, S, Z>> for XOnly {
fn eq(&self, rhs: &Point<T, S, Z>) -> bool {
rhs == self
}
}
crate::impl_fromstr_deserailize! {
name => "secp256k1 x-coordinate",
fn from_bytes(bytes: [u8;32]) -> Option<XOnly> {
XOnly::from_bytes(bytes)
}
}
crate::impl_display_debug_serialize! {
fn to_bytes(xonly: &XOnly) -> &[u8;32] {
xonly.as_bytes()
}
}
#[cfg(test)]
mod test {
use super::*;
crate::test_plus_wasm! {
fn xonly_random() {
let _ = XOnly::random(&mut rand::thread_rng());
}
fn from_str() {
use crate::G;
use core::str::FromStr;
assert_eq!(
XOnly::from_str(
"79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
)
.unwrap()
.to_point(),
*G
);
}
fn xonly_to_point() {
for _ in 0..crate::TEST_SOUNDNESS {
let xonly_even = XOnly::random(&mut rand::thread_rng());
let point_even = xonly_even.to_point();
assert!(point_even.is_y_even());
}
}
}
}