ree_types/
pubkey.rs

1use alloc::str::FromStr;
2use candid::{
3    CandidType,
4    types::{Serializer, Type, TypeInner},
5};
6use ic_stable_structures::{Storable, storable::Bound};
7
8/// The Bitcoin compressed public key compatible with the IC storage.
9#[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}