use std::fmt;
use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use thiserror::Error;
use crate::util::hex::{self, HexError};
pub const PUBLIC_KEY_SIZE: usize = 32;
#[derive(Debug, Clone, Copy, Error)]
#[non_exhaustive]
pub enum PublicKeyError {
#[error("invalid hex encoding: {0}")]
Hex(#[from] HexError),
#[error("invalid length: expected {PUBLIC_KEY_SIZE} bytes, got {0}")]
InvalidLength(usize),
#[error("not a valid x-only public key")]
InvalidPoint,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PublicKey(secp256k1::XOnlyPublicKey);
impl PublicKey {
pub fn from_byte_array(bytes: [u8; PUBLIC_KEY_SIZE]) -> Result<Self, PublicKeyError> {
secp256k1::XOnlyPublicKey::from_byte_array(bytes)
.map(Self)
.map_err(|_| PublicKeyError::InvalidPoint)
}
pub fn from_slice(bytes: &[u8]) -> Result<Self, PublicKeyError> {
let array: [u8; PUBLIC_KEY_SIZE] = bytes
.try_into()
.map_err(|_| PublicKeyError::InvalidLength(bytes.len()))?;
Self::from_byte_array(array)
}
pub fn parse<S>(input: S) -> Result<Self, PublicKeyError>
where
S: AsRef<str>,
{
let bytes = hex::decode(input.as_ref())?;
Self::from_slice(&bytes)
}
#[must_use]
pub fn to_byte_array(self) -> [u8; PUBLIC_KEY_SIZE] {
self.0.serialize()
}
#[must_use]
pub fn to_hex(self) -> String {
hex::encode(self.0.serialize())
}
#[must_use]
pub const fn as_inner(&self) -> &secp256k1::XOnlyPublicKey {
&self.0
}
#[must_use]
pub fn verify_schnorr(&self, message: &[u8; 32], sig: &secp256k1::schnorr::Signature) -> bool {
secp256k1::SECP256K1
.verify_schnorr(sig, message, &self.0)
.is_ok()
}
}
impl fmt::Debug for PublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("PublicKey").field(&self.to_hex()).finish()
}
}
impl fmt::Display for PublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
hex::fmt_lower(self.0.serialize(), f)
}
}
impl fmt::LowerHex for PublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
hex::fmt_lower(self.0.serialize(), f)
}
}
impl FromStr for PublicKey {
type Err = PublicKeyError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s)
}
}
impl From<secp256k1::XOnlyPublicKey> for PublicKey {
fn from(value: secp256k1::XOnlyPublicKey) -> Self {
Self(value)
}
}
impl Serialize for PublicKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(self)
}
}
impl<'de> Deserialize<'de> for PublicKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let raw = <&str>::deserialize(deserializer)?;
Self::parse(raw).map_err(serde::de::Error::custom)
}
}
#[cfg(test)]
mod tests {
use hex_literal::hex;
use super::*;
const G_X: [u8; 32] = hex!("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798");
#[test]
fn parse_lowercase_hex() {
let lower = "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798";
let pk = PublicKey::parse(lower).unwrap();
assert_eq!(pk.to_hex(), lower);
}
#[test]
fn from_byte_array_round_trip() {
let pk = PublicKey::from_byte_array(G_X).unwrap();
assert_eq!(pk.to_byte_array(), G_X);
}
#[test]
fn from_slice_wrong_length() {
let err = PublicKey::from_slice(&[0_u8; 16]).unwrap_err();
assert!(matches!(err, PublicKeyError::InvalidLength(16)));
}
#[test]
fn invalid_point_rejected() {
let bytes = hex!("0100000000000000000000000000000000000000000000000000000000000000");
let err = PublicKey::from_byte_array(bytes).unwrap_err();
assert!(matches!(err, PublicKeyError::InvalidPoint));
}
#[test]
fn display_lowercase() {
let pk = PublicKey::from_byte_array(G_X).unwrap();
let s = format!("{pk}");
assert_eq!(s.len(), 64);
assert!(
s.chars()
.all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase())
);
}
#[test]
fn debug_includes_hex() {
let pk = PublicKey::from_byte_array(G_X).unwrap();
let dbg = format!("{pk:?}");
assert!(dbg.contains(&pk.to_hex()));
}
#[test]
fn serde_round_trip() {
let pk = PublicKey::from_byte_array(G_X).unwrap();
let json = serde_json::to_string(&pk).unwrap();
let parsed: PublicKey = serde_json::from_str(&json).unwrap();
assert_eq!(parsed, pk);
}
#[test]
fn ordering_is_lexicographic() {
let lhs = PublicKey::from_byte_array(hex!(
"0000000000000000000000000000000000000000000000000000000000000002"
))
.unwrap();
let rhs = PublicKey::from_byte_array(hex!(
"0000000000000000000000000000000000000000000000000000000000000003"
))
.unwrap();
assert!(lhs < rhs);
}
#[test]
fn verify_schnorr_round_trip() {
use crate::Keys;
let keys = Keys::parse("0000000000000000000000000000000000000000000000000000000000000003")
.unwrap();
let message = hex!("0202020202020202020202020202020202020202020202020202020202020202");
let sig = keys.sign_schnorr(&message);
assert!(keys.public_key().verify_schnorr(&message, &sig));
let mut bad = message;
bad[0] ^= 0xff;
assert!(!keys.public_key().verify_schnorr(&bad, &sig));
}
}