litep2p/
peer_id.rs

1// Copyright 2018 Parity Technologies (UK) Ltd.
2// Copyright 2023 litep2p developers
3//
4// Permission is hereby granted, free of charge, to any person obtaining a
5// copy of this software and associated documentation files (the "Software"),
6// to deal in the Software without restriction, including without limitation
7// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8// and/or sell copies of the Software, and to permit persons to whom the
9// Software is furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20// DEALINGS IN THE SOFTWARE.
21
22#![allow(clippy::wrong_self_convention)]
23
24use crate::crypto::PublicKey;
25
26use multiaddr::{Multiaddr, Protocol};
27use multihash::{Code, Error, Multihash, MultihashDigest};
28use rand::Rng;
29use serde::{Deserialize, Serialize};
30use thiserror::Error;
31
32use std::{convert::TryFrom, fmt, str::FromStr};
33
34/// Public keys with byte-lengths smaller than `MAX_INLINE_KEY_LENGTH` will be
35/// automatically used as the peer id using an identity multihash.
36const MAX_INLINE_KEY_LENGTH: usize = 42;
37
38/// Identifier of a peer of the network.
39///
40/// The data is a CIDv0 compatible multihash of the protobuf encoded public key of the peer
41/// as specified in [specs/peer-ids](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md).
42#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
43pub struct PeerId {
44    multihash: Multihash,
45}
46
47impl fmt::Debug for PeerId {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        f.debug_tuple("PeerId").field(&self.to_base58()).finish()
50    }
51}
52
53impl fmt::Display for PeerId {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        self.to_base58().fmt(f)
56    }
57}
58
59impl PeerId {
60    /// Builds a `PeerId` from a public key.
61    pub fn from_public_key(key: &PublicKey) -> PeerId {
62        Self::from_public_key_protobuf(&key.to_protobuf_encoding())
63    }
64
65    /// Builds a `PeerId` from a public key in protobuf encoding.
66    pub fn from_public_key_protobuf(key_enc: &[u8]) -> PeerId {
67        let hash_algorithm = if key_enc.len() <= MAX_INLINE_KEY_LENGTH {
68            Code::Identity
69        } else {
70            Code::Sha2_256
71        };
72
73        let multihash = hash_algorithm.digest(key_enc);
74
75        PeerId { multihash }
76    }
77
78    /// Parses a `PeerId` from bytes.
79    pub fn from_bytes(data: &[u8]) -> Result<PeerId, Error> {
80        PeerId::from_multihash(Multihash::from_bytes(data)?)
81            .map_err(|mh| Error::UnsupportedCode(mh.code()))
82    }
83
84    /// Tries to turn a `Multihash` into a `PeerId`.
85    ///
86    /// If the multihash does not use a valid hashing algorithm for peer IDs,
87    /// or the hash value does not satisfy the constraints for a hashed
88    /// peer ID, it is returned as an `Err`.
89    pub fn from_multihash(multihash: Multihash) -> Result<PeerId, Multihash> {
90        match Code::try_from(multihash.code()) {
91            Ok(Code::Sha2_256) => Ok(PeerId { multihash }),
92            Ok(Code::Identity) if multihash.digest().len() <= MAX_INLINE_KEY_LENGTH =>
93                Ok(PeerId { multihash }),
94            _ => Err(multihash),
95        }
96    }
97
98    /// Tries to extract a [`PeerId`] from the given [`Multiaddr`].
99    ///
100    /// In case the given [`Multiaddr`] ends with `/p2p/<peer-id>`, this function
101    /// will return the encapsulated [`PeerId`], otherwise it will return `None`.
102    pub fn try_from_multiaddr(address: &Multiaddr) -> Option<PeerId> {
103        address.iter().last().and_then(|p| match p {
104            Protocol::P2p(hash) => PeerId::from_multihash(hash).ok(),
105            _ => None,
106        })
107    }
108
109    /// Generates a random peer ID from a cryptographically secure PRNG.
110    ///
111    /// This is useful for randomly walking on a DHT, or for testing purposes.
112    pub fn random() -> PeerId {
113        let peer_id = rand::thread_rng().gen::<[u8; 32]>();
114        PeerId {
115            multihash: Multihash::wrap(Code::Identity.into(), &peer_id)
116                .expect("The digest size is never too large"),
117        }
118    }
119
120    /// Returns a raw bytes representation of this `PeerId`.
121    pub fn to_bytes(&self) -> Vec<u8> {
122        self.multihash.to_bytes()
123    }
124
125    /// Returns a base-58 encoded string of this `PeerId`.
126    pub fn to_base58(&self) -> String {
127        bs58::encode(self.to_bytes()).into_string()
128    }
129
130    /// Checks whether the public key passed as parameter matches the public key of this `PeerId`.
131    ///
132    /// Returns `None` if this `PeerId`s hash algorithm is not supported when encoding the
133    /// given public key, otherwise `Some` boolean as the result of an equality check.
134    pub fn is_public_key(&self, public_key: &PublicKey) -> Option<bool> {
135        let alg = Code::try_from(self.multihash.code())
136            .expect("Internal multihash is always a valid `Code`");
137        let enc = public_key.to_protobuf_encoding();
138        Some(alg.digest(&enc) == self.multihash)
139    }
140}
141
142impl From<PublicKey> for PeerId {
143    fn from(key: PublicKey) -> PeerId {
144        PeerId::from_public_key(&key)
145    }
146}
147
148impl From<&PublicKey> for PeerId {
149    fn from(key: &PublicKey) -> PeerId {
150        PeerId::from_public_key(key)
151    }
152}
153
154impl TryFrom<Vec<u8>> for PeerId {
155    type Error = Vec<u8>;
156
157    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
158        PeerId::from_bytes(&value).map_err(|_| value)
159    }
160}
161
162impl TryFrom<Multihash> for PeerId {
163    type Error = Multihash;
164
165    fn try_from(value: Multihash) -> Result<Self, Self::Error> {
166        PeerId::from_multihash(value)
167    }
168}
169
170impl AsRef<Multihash> for PeerId {
171    fn as_ref(&self) -> &Multihash {
172        &self.multihash
173    }
174}
175
176impl From<PeerId> for Multihash {
177    fn from(peer_id: PeerId) -> Self {
178        peer_id.multihash
179    }
180}
181
182impl From<PeerId> for Vec<u8> {
183    fn from(peer_id: PeerId) -> Self {
184        peer_id.to_bytes()
185    }
186}
187
188impl Serialize for PeerId {
189    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
190    where
191        S: serde::Serializer,
192    {
193        if serializer.is_human_readable() {
194            serializer.serialize_str(&self.to_base58())
195        } else {
196            serializer.serialize_bytes(&self.to_bytes()[..])
197        }
198    }
199}
200
201impl<'de> Deserialize<'de> for PeerId {
202    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
203    where
204        D: serde::Deserializer<'de>,
205    {
206        use serde::de::*;
207
208        struct PeerIdVisitor;
209
210        impl Visitor<'_> for PeerIdVisitor {
211            type Value = PeerId;
212
213            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
214                write!(f, "valid peer id")
215            }
216
217            fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
218            where
219                E: Error,
220            {
221                PeerId::from_bytes(v).map_err(|_| Error::invalid_value(Unexpected::Bytes(v), &self))
222            }
223
224            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
225            where
226                E: Error,
227            {
228                PeerId::from_str(v).map_err(|_| Error::invalid_value(Unexpected::Str(v), &self))
229            }
230        }
231
232        if deserializer.is_human_readable() {
233            deserializer.deserialize_str(PeerIdVisitor)
234        } else {
235            deserializer.deserialize_bytes(PeerIdVisitor)
236        }
237    }
238}
239
240#[derive(Debug, Error)]
241pub enum ParseError {
242    #[error("base-58 decode error: {0}")]
243    B58(#[from] bs58::decode::Error),
244    #[error("decoding multihash failed")]
245    MultiHash,
246}
247
248impl FromStr for PeerId {
249    type Err = ParseError;
250
251    #[inline]
252    fn from_str(s: &str) -> Result<Self, Self::Err> {
253        let bytes = bs58::decode(s).into_vec()?;
254        PeerId::from_bytes(&bytes).map_err(|_| ParseError::MultiHash)
255    }
256}
257
258#[cfg(test)]
259mod tests {
260    use crate::{crypto::ed25519::Keypair, PeerId};
261    use multiaddr::{Multiaddr, Protocol};
262    use multihash::Multihash;
263
264    #[test]
265    fn peer_id_is_public_key() {
266        let key = Keypair::generate().public();
267        let peer_id = key.to_peer_id();
268        assert_eq!(peer_id.is_public_key(&key.into()), Some(true));
269    }
270
271    #[test]
272    fn peer_id_into_bytes_then_from_bytes() {
273        let peer_id = Keypair::generate().public().to_peer_id();
274        let second = PeerId::from_bytes(&peer_id.to_bytes()).unwrap();
275        assert_eq!(peer_id, second);
276    }
277
278    #[test]
279    fn peer_id_to_base58_then_back() {
280        let peer_id = Keypair::generate().public().to_peer_id();
281        let second: PeerId = peer_id.to_base58().parse().unwrap();
282        assert_eq!(peer_id, second);
283    }
284
285    #[test]
286    fn random_peer_id_is_valid() {
287        for _ in 0..5000 {
288            let peer_id = PeerId::random();
289            assert_eq!(peer_id, PeerId::from_bytes(&peer_id.to_bytes()).unwrap());
290        }
291    }
292
293    #[test]
294    fn peer_id_from_multiaddr() {
295        let address = "[::1]:1337".parse::<std::net::SocketAddr>().unwrap();
296        let peer = PeerId::random();
297        let address = Multiaddr::empty()
298            .with(Protocol::from(address.ip()))
299            .with(Protocol::Tcp(address.port()))
300            .with(Protocol::P2p(Multihash::from(peer)));
301
302        assert_eq!(peer, PeerId::try_from_multiaddr(&address).unwrap());
303    }
304
305    #[test]
306    fn peer_id_from_multiaddr_no_peer_id() {
307        let address = "[::1]:1337".parse::<std::net::SocketAddr>().unwrap();
308        let address = Multiaddr::empty()
309            .with(Protocol::from(address.ip()))
310            .with(Protocol::Tcp(address.port()));
311
312        assert!(PeerId::try_from_multiaddr(&address).is_none());
313    }
314
315    #[test]
316    fn peer_id_from_bytes() {
317        let peer = PeerId::random();
318        let bytes = peer.to_bytes();
319
320        assert_eq!(PeerId::try_from(bytes).unwrap(), peer);
321    }
322
323    #[test]
324    fn peer_id_as_multihash() {
325        let peer = PeerId::random();
326        let multihash = Multihash::from(peer);
327
328        assert_eq!(&multihash, peer.as_ref());
329        assert_eq!(PeerId::try_from(multihash).unwrap(), peer);
330    }
331
332    #[test]
333    fn serialize_deserialize() {
334        let peer = PeerId::random();
335        let serialized = serde_json::to_string(&peer).unwrap();
336        let deserialized = serde_json::from_str(&serialized).unwrap();
337
338        assert_eq!(peer, deserialized);
339    }
340
341    #[test]
342    fn invalid_multihash() {
343        fn test() -> crate::Result<PeerId> {
344            let bytes = [
345                0x16, 0x20, 0x64, 0x4b, 0xcc, 0x7e, 0x56, 0x43, 0x73, 0x04, 0x09, 0x99, 0xaa, 0xc8,
346                0x9e, 0x76, 0x22, 0xf3, 0xca, 0x71, 0xfb, 0xa1, 0xd9, 0x72, 0xfd, 0x94, 0xa3, 0x1c,
347                0x3b, 0xfb, 0xf2, 0x4e, 0x39, 0x38,
348            ];
349
350            PeerId::from_multihash(Multihash::from_bytes(&bytes).unwrap()).map_err(From::from)
351        }
352        let _error = test().unwrap_err();
353    }
354}