1use alloc::str::FromStr;
2use candid::{
3 CandidType,
4 types::{Serializer, Type, TypeInner},
5};
6use ic_stable_structures::{Storable, storable::Bound};
7
8#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug)]
10pub struct Pubkey(Vec<u8>);
11
12impl Pubkey {
13 pub fn from_raw(key: Vec<u8>) -> Result<Pubkey, String> {
14 if key.len() != 33 {
15 return Err("invalid pubkey".to_string());
16 }
17 Ok(Self(key))
18 }
19
20 pub fn as_bytes(&self) -> &[u8] {
21 &self.0
22 }
23
24 pub fn to_x_only_public_key(&self) -> bitcoin::XOnlyPublicKey {
25 bitcoin::XOnlyPublicKey::from_slice(&self.0[1..]).expect("The inner is 33 bytes")
26 }
27
28 pub fn to_public_key(&self) -> Result<bitcoin::PublicKey, String> {
29 match self.0[0] {
30 0x02 | 0x03 => {
31 Ok(bitcoin::PublicKey::from_slice(&self.0).expect("The inner is 33 bytes"))
32 }
33 _ => Err("the pubkey is deserialized from a XOnlyPublicKey".to_string()),
34 }
35 }
36}
37
38impl CandidType for Pubkey {
39 fn _ty() -> Type {
40 TypeInner::Text.into()
41 }
42
43 fn idl_serialize<S>(&self, serializer: S) -> Result<(), S::Error>
44 where
45 S: Serializer,
46 {
47 serializer.serialize_text(&self.to_string())
48 }
49}
50
51impl Storable for Pubkey {
52 const BOUND: Bound = Bound::Bounded {
53 max_size: 33,
54 is_fixed_size: true,
55 };
56
57 fn to_bytes(&self) -> alloc::borrow::Cow<'_, [u8]> {
58 alloc::borrow::Cow::Borrowed(&self.0)
59 }
60
61 fn into_bytes(self) -> Vec<u8> {
62 self.0
63 }
64
65 fn from_bytes(bytes: alloc::borrow::Cow<'_, [u8]>) -> Self {
66 Self::from_raw(bytes.to_vec()).expect("Couldn't deserialize pubkey from stable memory")
67 }
68}
69
70impl core::fmt::Display for Pubkey {
71 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
72 match self.0[0] {
73 0x02 | 0x03 => {
74 let key = bitcoin::PublicKey::from_slice(&self.0).expect("The inner is 33 bytes");
75 write!(f, "{}", key)
76 }
77 _ => {
78 let key = bitcoin::XOnlyPublicKey::from_slice(&self.0[1..])
79 .expect("The inner is 33 bytes");
80 write!(f, "{}", key)
81 }
82 }
83 }
84}
85
86impl FromStr for Pubkey {
87 type Err = String;
88
89 fn from_str(s: &str) -> Result<Self, Self::Err> {
90 let s = s.trim_start_matches("0x");
91 let raw = hex::decode(s).map_err(|_| "invalid pubkey".to_string())?;
92 if raw.len() == 32 {
93 let v = [&[0x00], &raw[..]].concat();
94 Self::from_raw(v)
95 } else {
96 Self::from_raw(raw)
97 }
98 }
99}
100
101struct PubkeyVisitor;
102
103impl<'de> serde::de::Visitor<'de> for PubkeyVisitor {
104 type Value = Pubkey;
105
106 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
107 write!(formatter, "a 33-bytes pubkey")
108 }
109
110 fn visit_str<E>(self, value: &str) -> Result<Pubkey, E>
111 where
112 E: serde::de::Error,
113 {
114 Pubkey::from_str(value)
115 .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self))
116 }
117}
118
119impl<'de> serde::Deserialize<'de> for Pubkey {
120 fn deserialize<D>(deserializer: D) -> Result<Pubkey, D::Error>
121 where
122 D: serde::de::Deserializer<'de>,
123 {
124 deserializer.deserialize_str(PubkeyVisitor)
125 }
126}
127
128impl serde::Serialize for Pubkey {
129 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
130 where
131 S: serde::ser::Serializer,
132 {
133 serializer.serialize_str(&self.to_string())
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn test_bincode_serde() {
143 let pubkey_hex = "007c06bc45a24f098e327b1e27ed5a9b4477b58c3bbfed5a3bb36c6f59bda290b2";
144 let pubkey_bytes = hex::decode(pubkey_hex).unwrap();
145 let pubkey = Pubkey(pubkey_bytes);
146 let encoded_hex = hex::encode(bincode::serialize(&pubkey).unwrap());
147 assert_eq!(
148 pubkey.0,
149 bincode::deserialize::<Pubkey>(&hex::decode(encoded_hex).unwrap())
150 .unwrap()
151 .0
152 );
153 }
154}