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