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 _ => Err(multihash),
83 }
84 }
85
86 pub fn from_bytes(data: &[u8]) -> Result<PeerId, Error> {
88 PeerId::from_multihash(Multihash::from_bytes(data)?)
89 .map_err(|mh| Error::UnsupportedCode(mh.code()))
90 }
91
92 pub fn to_bytes(&self) -> Vec<u8> {
94 self.multihash.to_bytes()
95 }
96
97 pub fn to_base58(&self) -> String {
99 bs58::encode(self.to_bytes()).into_string()
100 }
101
102 pub fn into_ed25519(&self) -> Option<[u8; 32]> {
104 let hash = &self.multihash;
105 if hash.code() != 0 {
107 return None
109 }
110
111 let public = libp2p_identity::PublicKey::try_decode_protobuf(hash.digest()).ok()?;
112 public.try_into_ed25519().ok().map(|public| public.to_bytes())
113 }
114
115 pub fn from_ed25519(bytes: &[u8; 32]) -> Option<PeerId> {
117 let public = libp2p_identity::ed25519::PublicKey::try_from_bytes(bytes).ok()?;
118 let public: libp2p_identity::PublicKey = public.into();
119 let peer_id: libp2p_identity::PeerId = public.into();
120
121 Some(peer_id.into())
122 }
123}
124
125impl AsRef<Multihash> for PeerId {
126 fn as_ref(&self) -> &Multihash {
127 &self.multihash
128 }
129}
130
131impl From<PeerId> for Multihash {
132 fn from(peer_id: PeerId) -> Self {
133 peer_id.multihash
134 }
135}
136
137impl From<libp2p_identity::PeerId> for PeerId {
138 fn from(peer_id: libp2p_identity::PeerId) -> Self {
139 PeerId { multihash: Multihash::from_bytes(&peer_id.to_bytes()).expect("to succeed") }
140 }
141}
142
143impl From<PeerId> for libp2p_identity::PeerId {
144 fn from(peer_id: PeerId) -> Self {
145 libp2p_identity::PeerId::from_bytes(&peer_id.to_bytes()).expect("to succeed")
146 }
147}
148
149impl From<&libp2p_identity::PeerId> for PeerId {
150 fn from(peer_id: &libp2p_identity::PeerId) -> Self {
151 PeerId { multihash: Multihash::from_bytes(&peer_id.to_bytes()).expect("to succeed") }
152 }
153}
154
155impl From<&PeerId> for libp2p_identity::PeerId {
156 fn from(peer_id: &PeerId) -> Self {
157 libp2p_identity::PeerId::from_bytes(&peer_id.to_bytes()).expect("to succeed")
158 }
159}
160
161impl From<litep2p::PeerId> for PeerId {
162 fn from(peer_id: litep2p::PeerId) -> Self {
163 PeerId { multihash: Multihash::from_bytes(&peer_id.to_bytes()).expect("to succeed") }
164 }
165}
166
167impl From<PeerId> for litep2p::PeerId {
168 fn from(peer_id: PeerId) -> Self {
169 litep2p::PeerId::from_bytes(&peer_id.to_bytes()).expect("to succeed")
170 }
171}
172
173impl From<&litep2p::PeerId> for PeerId {
174 fn from(peer_id: &litep2p::PeerId) -> Self {
175 PeerId { multihash: Multihash::from_bytes(&peer_id.to_bytes()).expect("to succeed") }
176 }
177}
178
179impl From<&PeerId> for litep2p::PeerId {
180 fn from(peer_id: &PeerId) -> Self {
181 litep2p::PeerId::from_bytes(&peer_id.to_bytes()).expect("to succeed")
182 }
183}
184
185#[derive(Debug, thiserror::Error)]
187pub enum ParseError {
188 #[error("base-58 decode error: {0}")]
189 B58(#[from] bs58::decode::Error),
190 #[error("unsupported multihash code '{0}'")]
191 UnsupportedCode(u64),
192 #[error("invalid multihash")]
193 InvalidMultihash(#[from] crate::multihash::Error),
194}
195
196impl FromStr for PeerId {
197 type Err = ParseError;
198
199 #[inline]
200 fn from_str(s: &str) -> Result<Self, Self::Err> {
201 let bytes = bs58::decode(s).into_vec()?;
202 let peer_id = PeerId::from_bytes(&bytes)?;
203
204 Ok(peer_id)
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn extract_peer_id_from_multiaddr() {
214 {
215 let peer = PeerId::random();
216 let address = "/ip4/198.51.100.19/tcp/30333"
217 .parse::<Multiaddr>()
218 .unwrap()
219 .with(Protocol::P2p(peer.into()));
220
221 assert_eq!(PeerId::try_from_multiaddr(&address), Some(peer));
222 }
223
224 {
225 let peer = PeerId::random();
226 assert_eq!(
227 PeerId::try_from_multiaddr(&Multiaddr::empty().with(Protocol::P2p(peer.into()))),
228 Some(peer)
229 );
230 }
231
232 {
233 assert!(PeerId::try_from_multiaddr(
234 &"/ip4/198.51.100.19/tcp/30333".parse::<Multiaddr>().unwrap()
235 )
236 .is_none());
237 }
238 }
239
240 #[test]
241 fn from_ed25519() {
242 let keypair = litep2p::crypto::ed25519::Keypair::generate();
243 let original_peer_id = litep2p::PeerId::from_public_key(
244 &litep2p::crypto::PublicKey::Ed25519(keypair.public()),
245 );
246
247 let peer_id: PeerId = original_peer_id.into();
248 assert_eq!(original_peer_id.to_bytes(), peer_id.to_bytes());
249
250 let key = peer_id.into_ed25519().unwrap();
251 assert_eq!(PeerId::from_ed25519(&key).unwrap(), original_peer_id.into());
252 }
253}