Skip to main content

cashu/nuts/nut01/
public_key.rs

1use core::fmt;
2use core::ops::Deref;
3use core::str::FromStr;
4
5use bitcoin::hashes::sha256::Hash as Sha256Hash;
6use bitcoin::hashes::Hash;
7use bitcoin::secp256k1::schnorr::Signature;
8use bitcoin::secp256k1::{self, Message, XOnlyPublicKey};
9use serde::{Deserialize, Deserializer, Serialize};
10
11use super::Error;
12use crate::SECP256K1;
13
14/// PublicKey
15#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
16pub struct PublicKey {
17    inner: secp256k1::PublicKey,
18}
19
20impl fmt::Debug for PublicKey {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        write!(f, "PublicKey({})", self.to_hex())
23    }
24}
25
26impl Deref for PublicKey {
27    type Target = secp256k1::PublicKey;
28
29    fn deref(&self) -> &Self::Target {
30        &self.inner
31    }
32}
33
34impl From<secp256k1::PublicKey> for PublicKey {
35    fn from(inner: secp256k1::PublicKey) -> Self {
36        Self { inner }
37    }
38}
39
40impl PublicKey {
41    /// Parse from `bytes`
42    #[inline]
43    pub fn from_slice(slice: &[u8]) -> Result<Self, Error> {
44        Ok(Self {
45            inner: secp256k1::PublicKey::from_slice(slice)?,
46        })
47    }
48
49    /// Parse from `hex` string
50    #[inline]
51    pub fn from_hex<S>(hex: S) -> Result<Self, Error>
52    where
53        S: AsRef<str>,
54    {
55        let hex: &str = hex.as_ref();
56
57        // Check size
58        if hex.len() != 33 * 2 {
59            return Err(Error::InvalidPublicKeySize {
60                expected: 33,
61                found: hex.len() / 2,
62            });
63        }
64
65        Ok(Self {
66            inner: secp256k1::PublicKey::from_str(hex)?,
67        })
68    }
69
70    /// [`PublicKey`] to bytes
71    #[inline]
72    pub fn to_bytes(&self) -> [u8; 33] {
73        self.inner.serialize()
74    }
75
76    /// To uncompressed bytes
77    #[inline]
78    pub fn to_uncompressed_bytes(&self) -> [u8; 65] {
79        self.inner.serialize_uncompressed()
80    }
81
82    /// To [`XOnlyPublicKey`]
83    #[inline]
84    pub fn x_only_public_key(&self) -> XOnlyPublicKey {
85        self.inner.x_only_public_key().0
86    }
87
88    /// Get public key as `hex` string
89    #[inline]
90    pub fn to_hex(&self) -> String {
91        self.inner.to_string()
92    }
93
94    /// Verify schnorr signature
95    pub fn verify(&self, msg: &[u8], sig: &Signature) -> Result<(), Error> {
96        let hash: Sha256Hash = Sha256Hash::hash(msg);
97        let msg = Message::from_digest_slice(hash.as_ref())?;
98        SECP256K1.verify_schnorr(sig, &msg, &self.inner.x_only_public_key().0)?;
99        Ok(())
100    }
101}
102
103impl FromStr for PublicKey {
104    type Err = Error;
105
106    fn from_str(hex: &str) -> Result<Self, Self::Err> {
107        Self::from_hex(hex)
108    }
109}
110
111impl fmt::Display for PublicKey {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        write!(f, "{}", self.to_hex())
114    }
115}
116
117impl Serialize for PublicKey {
118    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
119    where
120        S: serde::Serializer,
121    {
122        serializer.serialize_str(&self.to_hex())
123    }
124}
125
126impl<'de> Deserialize<'de> for PublicKey {
127    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
128    where
129        D: Deserializer<'de>,
130    {
131        let public_key: String = String::deserialize(deserializer)?;
132        Self::from_hex(public_key).map_err(serde::de::Error::custom)
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[test]
141    fn test_public_key_from_hex() {
142        // Compressed
143        assert!(PublicKey::from_hex(
144            "02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104"
145        )
146        .is_ok());
147    }
148
149    #[test]
150    fn test_invalid_public_key_from_hex() {
151        // Uncompressed (is valid but is cashu must be compressed?)
152        assert!(PublicKey::from_hex("04fd4ce5a16b65576145949e6f99f445f8249fee17c606b688b504a849cdc452de3625246cb2c27dac965cb7200a5986467eee92eb7d496bbf1453b074e223e481")
153            .is_err())
154    }
155}
156
157#[cfg(all(feature = "bench", test))]
158mod benches {
159    extern crate test;
160    use test::{black_box, Bencher};
161
162    use super::*;
163
164    const HEX: &str = "02194603ffa36356f4a56b7df9371fc3192472351453ec7398b8da8117e7c3e104";
165
166    #[bench]
167    pub fn public_key_from_hex(bh: &mut Bencher) {
168        bh.iter(|| {
169            black_box(PublicKey::from_hex(HEX)).unwrap();
170        });
171    }
172}