p2p/identity/
peer_id.rs

1use std::{fmt, str::FromStr};
2
3use binprot::{BinProtRead, BinProtWrite, Nat0};
4use libp2p_identity::DecodingError;
5use malloc_size_of_derive::MallocSizeOf;
6use serde::{Deserialize, Serialize};
7
8use super::PublicKey;
9
10#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Copy, MallocSizeOf)]
11pub struct PeerId([u64; 4]);
12
13impl PeerId {
14    const BASE58_CHECK_VERSION: u8 = 0x2F; // 'p'
15
16    pub fn from_bytes(bytes: [u8; 32]) -> Self {
17        let mut chunk0: [u8; 8] = [0; 8];
18        let mut chunk1: [u8; 8] = [0; 8];
19        let mut chunk2: [u8; 8] = [0; 8];
20        let mut chunk3: [u8; 8] = [0; 8];
21
22        chunk0.copy_from_slice(&bytes[0..8]);
23        chunk1.copy_from_slice(&bytes[8..16]);
24        chunk2.copy_from_slice(&bytes[16..24]);
25        chunk3.copy_from_slice(&bytes[24..32]);
26
27        Self([
28            u64::from_be_bytes(chunk0),
29            u64::from_be_bytes(chunk1),
30            u64::from_be_bytes(chunk2),
31            u64::from_be_bytes(chunk3),
32        ])
33    }
34
35    pub fn to_bytes(self) -> [u8; 32] {
36        let mut result: [u8; 32] = [0; 32];
37        result[0..8].copy_from_slice(&self.0[0].to_be_bytes());
38        result[8..16].copy_from_slice(&self.0[1].to_be_bytes());
39        result[16..24].copy_from_slice(&self.0[2].to_be_bytes());
40        result[24..32].copy_from_slice(&self.0[3].to_be_bytes());
41        result
42    }
43
44    pub fn from_public_key(key: PublicKey) -> Self {
45        Self::from_bytes(key.to_bytes())
46    }
47
48    pub fn to_public_key(self) -> Result<PublicKey, ed25519_dalek::SignatureError> {
49        PublicKey::from_bytes(self.to_bytes())
50    }
51
52    #[cfg(not(target_arch = "wasm32"))]
53    pub fn to_libp2p_string(self) -> String {
54        if let Ok(peer_id) = libp2p_identity::PeerId::try_from(self) {
55            peer_id.to_string()
56        } else {
57            "INVALID PEER_ID".to_string()
58        }
59    }
60}
61
62impl fmt::Display for PeerId {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        let s = bs58::encode(&self.to_bytes())
65            .with_check_version(Self::BASE58_CHECK_VERSION)
66            .into_string();
67        write!(f, "{}", s)
68    }
69}
70
71impl fmt::Debug for PeerId {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        write!(f, "PeerId({})", self)
74    }
75}
76
77#[derive(Clone, Debug, PartialEq, thiserror::Error, Serialize, Deserialize, MallocSizeOf)]
78pub enum PeerIdFromLibp2pPeerId {
79    #[error("error decoding public key from protobuf: {0}")]
80    Protobuf(String),
81    #[error("error converting public key to ed25519: {0}")]
82    Ed25519(String),
83    #[error("peer_id with unsupported multihash code")]
84    Code,
85}
86
87impl From<libp2p_identity::DecodingError> for PeerIdFromLibp2pPeerId {
88    fn from(value: libp2p_identity::DecodingError) -> Self {
89        PeerIdFromLibp2pPeerId::Protobuf(value.to_string())
90    }
91}
92
93impl From<libp2p_identity::OtherVariantError> for PeerIdFromLibp2pPeerId {
94    fn from(value: libp2p_identity::OtherVariantError) -> Self {
95        PeerIdFromLibp2pPeerId::Ed25519(value.to_string())
96    }
97}
98
99impl FromStr for PeerId {
100    type Err = bs58::decode::Error;
101
102    fn from_str(s: &str) -> Result<Self, Self::Err> {
103        let mut bytes = [0u8; 37];
104
105        let size = bs58::decode(s)
106            .with_check(Some(Self::BASE58_CHECK_VERSION))
107            .into(&mut bytes)?;
108        if size != 33 {
109            return Err(bs58::decode::Error::BufferTooSmall);
110        }
111        Ok(Self::from_bytes(
112            bytes[1..33].try_into().expect("Size checked above"),
113        ))
114    }
115}
116
117impl From<PeerId> for [u8; 32] {
118    fn from(value: PeerId) -> Self {
119        value.to_bytes()
120    }
121}
122
123impl TryFrom<libp2p_identity::PeerId> for PeerId {
124    type Error = PeerIdFromLibp2pPeerId;
125
126    fn try_from(value: libp2p_identity::PeerId) -> Result<Self, Self::Error> {
127        let slice = value.as_ref().digest();
128        if value.as_ref().code() == 0x12 {
129            return Err(PeerIdFromLibp2pPeerId::Code);
130        };
131        let key = libp2p_identity::PublicKey::try_decode_protobuf(slice)?;
132        let bytes = key.try_into_ed25519()?.to_bytes();
133        Ok(PeerId::from_bytes(bytes))
134    }
135}
136
137impl TryFrom<PeerId> for libp2p_identity::PeerId {
138    type Error = DecodingError;
139
140    fn try_from(value: PeerId) -> Result<Self, Self::Error> {
141        let key = libp2p_identity::ed25519::PublicKey::try_from_bytes(&value.to_bytes())?;
142        #[allow(deprecated)]
143        let key = libp2p_identity::PublicKey::from(key);
144        Ok(key.to_peer_id())
145    }
146}
147
148impl Serialize for PeerId {
149    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
150    where
151        S: serde::Serializer,
152    {
153        if serializer.is_human_readable() {
154            serializer.serialize_str(&self.to_string())
155        } else {
156            self.0.serialize(serializer)
157        }
158    }
159}
160
161impl<'de> serde::Deserialize<'de> for PeerId {
162    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
163    where
164        D: serde::Deserializer<'de>,
165    {
166        if deserializer.is_human_readable() {
167            let b58: String = Deserialize::deserialize(deserializer)?;
168            Ok(b58.parse().map_err(serde::de::Error::custom)?)
169        } else {
170            Ok(Self(Deserialize::deserialize(deserializer)?))
171        }
172    }
173}
174
175impl BinProtWrite for PeerId {
176    fn binprot_write<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
177        for v in self.0 {
178            Nat0(v).binprot_write(w)?;
179        }
180        Ok(())
181    }
182}
183
184impl BinProtRead for PeerId {
185    fn binprot_read<R: std::io::Read + ?Sized>(r: &mut R) -> Result<Self, binprot::Error>
186    where
187        Self: Sized,
188    {
189        Ok(Self([
190            Nat0::binprot_read(r)?.0,
191            Nat0::binprot_read(r)?.0,
192            Nat0::binprot_read(r)?.0,
193            Nat0::binprot_read(r)?.0,
194        ]))
195    }
196}
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201
202    #[test]
203    fn test_peer_id_bs58() {
204        let s = "2bEgBrPTzL8wov2D4Kz34WVLCxR4uCarsBmHYXWKQA5wvBQzd9H";
205        let id: PeerId = s.parse().expect("Parsing failed");
206        assert_eq!(s, id.to_string());
207    }
208
209    #[test]
210    fn test_libp2p_peer_id_conv() {
211        let s = "12D3KooWEiGVAFC7curXWXiGZyMWnZK9h8BKr88U8D5PKV3dXciv";
212        let id: libp2p_identity::PeerId = s.parse().expect("Parsing failed");
213        let conv: PeerId = id.try_into().expect("Parsing failed");
214        let id_conv: libp2p_identity::PeerId = conv.try_into().expect("Parsing failed");
215        assert_eq!(id_conv, id);
216    }
217
218    #[test]
219    #[ignore = "doesn't work"]
220    fn test_bare_base58btc_pk() {
221        let s = "QmSXffHzFVSEoQCYBS1bPpCn4vgGEpQnCA9NLYuhamPBU3";
222        let id: libp2p_identity::PeerId = s.parse().expect("Error parsing");
223        let conv: PeerId = id.try_into().expect("Error converting");
224        let id_conv: libp2p_identity::PeerId = conv.try_into().expect("Error converting");
225        assert_eq!(id_conv, id);
226    }
227}