1use crate::error::TaprootError;
6use secp256k1::{Secp256k1, XOnlyPublicKey as Secp256k1XOnlyPublicKey};
7use std::fmt;
8
9#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
11pub enum Parity {
12 Even,
14 Odd,
16}
17
18impl Parity {
19 pub fn to_u8(self) -> u8 {
21 match self {
22 Parity::Even => 0,
23 Parity::Odd => 1,
24 }
25 }
26
27 pub fn from_u8(value: u8) -> Result<Self, TaprootError> {
29 match value {
30 0 => Ok(Parity::Even),
31 1 => Ok(Parity::Odd),
32 _ => Err(TaprootError::InvalidParity),
33 }
34 }
35}
36
37impl From<secp256k1::Parity> for Parity {
38 fn from(p: secp256k1::Parity) -> Self {
39 match p {
40 secp256k1::Parity::Even => Parity::Even,
41 secp256k1::Parity::Odd => Parity::Odd,
42 }
43 }
44}
45
46impl From<Parity> for secp256k1::Parity {
47 fn from(p: Parity) -> Self {
48 match p {
49 Parity::Even => secp256k1::Parity::Even,
50 Parity::Odd => secp256k1::Parity::Odd,
51 }
52 }
53}
54
55#[derive(Clone, Copy, PartialEq, Eq, Hash)]
57pub struct XOnlyPublicKey {
58 inner: Secp256k1XOnlyPublicKey,
59}
60
61impl XOnlyPublicKey {
62 pub fn from_compressed(data: &[u8; 33]) -> Result<(Self, Parity), TaprootError> {
64 let pubkey = secp256k1::PublicKey::from_slice(data)
65 .map_err(|e| TaprootError::Secp256k1Error(e.to_string()))?;
66 let (xonly, parity) = pubkey.x_only_public_key();
67 Ok((Self { inner: xonly }, parity.into()))
68 }
69
70 pub fn from_slice(data: &[u8]) -> Result<Self, TaprootError> {
72 if data.len() != 32 {
73 return Err(TaprootError::InvalidLength {
74 expected: 32,
75 got: data.len(),
76 });
77 }
78 let inner = Secp256k1XOnlyPublicKey::from_slice(data)
79 .map_err(|e| TaprootError::Secp256k1Error(e.to_string()))?;
80 Ok(Self { inner })
81 }
82
83 pub fn from_bytes(data: [u8; 32]) -> Result<Self, TaprootError> {
85 Self::from_slice(&data)
86 }
87
88 pub fn serialize(&self) -> [u8; 32] {
90 self.inner.serialize()
91 }
92
93 pub fn as_bytes(&self) -> &[u8] {
95 unsafe {
98 std::slice::from_raw_parts(
99 &self.inner as *const _ as *const u8,
100 32,
101 )
102 }
103 }
104
105 pub fn tweak_add(
107 &self,
108 secp: &Secp256k1<secp256k1::All>,
109 tweak: &[u8; 32],
110 ) -> Result<(Self, Parity), TaprootError> {
111 let scalar = secp256k1::Scalar::from_be_bytes(*tweak)
112 .map_err(|e| TaprootError::InvalidTweak(e.to_string()))?;
113 let (tweaked, parity) = self.inner.add_tweak(secp, &scalar)
114 .map_err(|e| TaprootError::InvalidTweak(e.to_string()))?;
115 Ok((Self { inner: tweaked }, parity.into()))
116 }
117
118 pub fn inner(&self) -> &Secp256k1XOnlyPublicKey {
120 &self.inner
121 }
122
123 pub fn from_inner(inner: Secp256k1XOnlyPublicKey) -> Self {
125 Self { inner }
126 }
127
128 pub fn to_public_key(&self) -> secp256k1::PublicKey {
130 self.inner.public_key(secp256k1::Parity::Even)
131 }
132}
133
134impl fmt::Debug for XOnlyPublicKey {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 write!(f, "XOnlyPublicKey({})", hex::encode(self.serialize()))
137 }
138}
139
140impl fmt::Display for XOnlyPublicKey {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142 write!(f, "{}", hex::encode(self.serialize()))
143 }
144}
145
146impl std::str::FromStr for XOnlyPublicKey {
147 type Err = TaprootError;
148
149 fn from_str(s: &str) -> Result<Self, Self::Err> {
150 let bytes = hex::decode(s)
151 .map_err(|_| TaprootError::InvalidXOnlyKey)?;
152 Self::from_slice(&bytes)
153 }
154}
155
156
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn test_xonly_from_compressed() {
164 let compressed = [
166 0x02, 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac,
168 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87, 0x0b, 0x07,
169 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9,
170 0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98,
171 ];
172
173 let (xonly, parity) = XOnlyPublicKey::from_compressed(&compressed).unwrap();
174 assert_eq!(parity, Parity::Even);
175 assert_eq!(xonly.serialize()[..], compressed[1..]);
176 }
177
178 #[test]
179 fn test_xonly_roundtrip() {
180 let bytes = [
181 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac,
182 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87, 0x0b, 0x07,
183 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9,
184 0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98,
185 ];
186
187 let xonly = XOnlyPublicKey::from_bytes(bytes).unwrap();
188 assert_eq!(xonly.serialize(), bytes);
189 }
190
191 #[test]
192 fn test_xonly_display_parse() {
193 let bytes = [
194 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac,
195 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87, 0x0b, 0x07,
196 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9,
197 0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98,
198 ];
199
200 let xonly = XOnlyPublicKey::from_bytes(bytes).unwrap();
201 let hex_str = xonly.to_string();
202 let parsed: XOnlyPublicKey = hex_str.parse().unwrap();
203 assert_eq!(xonly, parsed);
204 }
205
206 #[test]
207 fn test_parity_conversion() {
208 assert_eq!(Parity::Even.to_u8(), 0);
209 assert_eq!(Parity::Odd.to_u8(), 1);
210 assert_eq!(Parity::from_u8(0).unwrap(), Parity::Even);
211 assert_eq!(Parity::from_u8(1).unwrap(), Parity::Odd);
212 assert!(Parity::from_u8(2).is_err());
213 }
214}