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; 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}