sc_network_types/
peer_id.rs1use crate::{
20 multiaddr::{Multiaddr, Protocol},
21 multihash::{Code, Error, Multihash},
22};
23use rand::Rng;
24use serde_with::{DeserializeFromStr, SerializeDisplay};
25
26use std::{fmt, hash::Hash, str::FromStr};
27
28const MAX_INLINE_KEY_LENGTH: usize = 42;
31
32#[derive(
37 Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd, SerializeDisplay, DeserializeFromStr,
38)]
39pub struct PeerId {
40 multihash: Multihash,
41}
42
43impl fmt::Debug for PeerId {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 f.debug_tuple("PeerId").field(&self.to_base58()).finish()
46 }
47}
48
49impl fmt::Display for PeerId {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 self.to_base58().fmt(f)
52 }
53}
54
55impl PeerId {
56 pub fn random() -> PeerId {
58 let peer = rand::thread_rng().gen::<[u8; 32]>();
59 PeerId {
60 multihash: Multihash::wrap(0x0, &peer).expect("The digest size is never too large"),
61 }
62 }
63
64 pub fn try_from_multiaddr(address: &Multiaddr) -> Option<PeerId> {
66 match address.iter().find(|protocol| std::matches!(protocol, Protocol::P2p(_))) {
67 Some(Protocol::P2p(multihash)) => Some(Self { multihash }),
68 _ => None,
69 }
70 }
71
72 pub fn from_multihash(multihash: Multihash) -> Result<PeerId, Multihash> {
78 match Code::try_from(multihash.code()) {
79 Ok(Code::Sha2_256) => Ok(PeerId { multihash }),
80 Ok(Code::Identity) if multihash.digest().len() <= MAX_INLINE_KEY_LENGTH => {
81 Ok(PeerId { multihash })
82 },
83 _ => Err(multihash),
84 }
85 }
86
87 pub fn from_bytes(data: &[u8]) -> Result<PeerId, Error> {
89 PeerId::from_multihash(Multihash::from_bytes(data)?)
90 .map_err(|mh| Error::UnsupportedCode(mh.code()))
91 }
92
93 pub fn to_bytes(&self) -> Vec<u8> {
95 self.multihash.to_bytes()
96 }
97
98 pub fn to_base58(&self) -> String {
100 bs58::encode(self.to_bytes()).into_string()
101 }
102
103 pub fn into_ed25519(&self) -> Option<[u8; 32]> {
105 let hash = &self.multihash;
106 if hash.code() != 0 {
108 return None;
110 }
111
112 let public = libp2p_identity::PublicKey::try_decode_protobuf(hash.digest()).ok()?;
113 public.try_into_ed25519().ok().map(|public| public.to_bytes())
114 }
115
116 pub fn from_ed25519(bytes: &[u8; 32]) -> Option<PeerId> {
118 let public = libp2p_identity::ed25519::PublicKey::try_from_bytes(bytes).ok()?;
119 let public: libp2p_identity::PublicKey = public.into();
120 let peer_id: libp2p_identity::PeerId = public.into();
121
122 Some(peer_id.into())
123 }
124}
125
126impl AsRef<Multihash> for PeerId {
127 fn as_ref(&self) -> &Multihash {
128 &self.multihash
129 }
130}
131
132impl From<PeerId> for Multihash {
133 fn from(peer_id: PeerId) -> Self {
134 peer_id.multihash
135 }
136}
137
138impl From<libp2p_identity::PeerId> for PeerId {
139 fn from(peer_id: libp2p_identity::PeerId) -> Self {
140 PeerId { multihash: Multihash::from_bytes(&peer_id.to_bytes()).expect("to succeed") }
141 }
142}
143
144impl From<PeerId> for libp2p_identity::PeerId {
145 fn from(peer_id: PeerId) -> Self {
146 libp2p_identity::PeerId::from_bytes(&peer_id.to_bytes()).expect("to succeed")
147 }
148}
149
150impl From<&libp2p_identity::PeerId> for PeerId {
151 fn from(peer_id: &libp2p_identity::PeerId) -> Self {
152 PeerId { multihash: Multihash::from_bytes(&peer_id.to_bytes()).expect("to succeed") }
153 }
154}
155
156impl From<&PeerId> for libp2p_identity::PeerId {
157 fn from(peer_id: &PeerId) -> Self {
158 libp2p_identity::PeerId::from_bytes(&peer_id.to_bytes()).expect("to succeed")
159 }
160}
161
162impl From<litep2p::PeerId> for PeerId {
163 fn from(peer_id: litep2p::PeerId) -> Self {
164 PeerId { multihash: Multihash::from_bytes(&peer_id.to_bytes()).expect("to succeed") }
165 }
166}
167
168impl From<PeerId> for litep2p::PeerId {
169 fn from(peer_id: PeerId) -> Self {
170 litep2p::PeerId::from_bytes(&peer_id.to_bytes()).expect("to succeed")
171 }
172}
173
174impl From<&litep2p::PeerId> for PeerId {
175 fn from(peer_id: &litep2p::PeerId) -> Self {
176 PeerId { multihash: Multihash::from_bytes(&peer_id.to_bytes()).expect("to succeed") }
177 }
178}
179
180impl From<&PeerId> for litep2p::PeerId {
181 fn from(peer_id: &PeerId) -> Self {
182 litep2p::PeerId::from_bytes(&peer_id.to_bytes()).expect("to succeed")
183 }
184}
185
186#[derive(Debug, thiserror::Error)]
188pub enum ParseError {
189 #[error("base-58 decode error: {0}")]
190 B58(#[from] bs58::decode::Error),
191 #[error("unsupported multihash code '{0}'")]
192 UnsupportedCode(u64),
193 #[error("invalid multihash")]
194 InvalidMultihash(#[from] crate::multihash::Error),
195}
196
197impl FromStr for PeerId {
198 type Err = ParseError;
199
200 #[inline]
201 fn from_str(s: &str) -> Result<Self, Self::Err> {
202 let bytes = bs58::decode(s).into_vec()?;
203 let peer_id = PeerId::from_bytes(&bytes)?;
204
205 Ok(peer_id)
206 }
207}
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212
213 #[test]
214 fn extract_peer_id_from_multiaddr() {
215 {
216 let peer = PeerId::random();
217 let address = "/ip4/198.51.100.19/tcp/30333"
218 .parse::<Multiaddr>()
219 .unwrap()
220 .with(Protocol::P2p(peer.into()));
221
222 assert_eq!(PeerId::try_from_multiaddr(&address), Some(peer));
223 }
224
225 {
226 let peer = PeerId::random();
227 assert_eq!(
228 PeerId::try_from_multiaddr(&Multiaddr::empty().with(Protocol::P2p(peer.into()))),
229 Some(peer)
230 );
231 }
232
233 {
234 assert!(PeerId::try_from_multiaddr(
235 &"/ip4/198.51.100.19/tcp/30333".parse::<Multiaddr>().unwrap()
236 )
237 .is_none());
238 }
239 }
240
241 #[test]
242 fn from_ed25519() {
243 let keypair = litep2p::crypto::ed25519::Keypair::generate();
244 let original_peer_id = litep2p::PeerId::from_public_key(
245 &litep2p::crypto::PublicKey::Ed25519(keypair.public()),
246 );
247
248 let peer_id: PeerId = original_peer_id.into();
249 assert_eq!(original_peer_id.to_bytes(), peer_id.to_bytes());
250
251 let key = peer_id.into_ed25519().unwrap();
252 assert_eq!(PeerId::from_ed25519(&key).unwrap(), original_peer_id.into());
253 }
254}