reth_network_peers/
lib.rs1#![doc(
49 html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
50 html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
51 issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
52)]
53#![cfg_attr(not(test), warn(unused_crate_dependencies))]
54#![cfg_attr(docsrs, feature(doc_cfg))]
55#![cfg_attr(not(feature = "std"), no_std)]
56
57extern crate alloc;
58
59use alloc::{
60 format,
61 string::{String, ToString},
62};
63use alloy_primitives::B512;
64use core::str::FromStr;
65
66#[cfg(feature = "secp256k1")]
68pub use enr::Enr;
69
70pub type PeerId = B512;
72
73pub mod node_record;
74pub use node_record::{NodeRecord, NodeRecordParseError};
75
76pub mod trusted_peer;
77pub use trusted_peer::TrustedPeer;
78
79mod bootnodes;
80pub use bootnodes::*;
81
82#[cfg(feature = "secp256k1")]
89const SECP256K1_TAG_PUBKEY_UNCOMPRESSED: u8 = 4;
90
91#[cfg(feature = "secp256k1")]
94#[inline]
95pub fn pk2id(pk: &secp256k1::PublicKey) -> PeerId {
96 PeerId::from_slice(&pk.serialize_uncompressed()[1..])
97}
98
99#[cfg(feature = "secp256k1")]
102#[inline]
103pub fn id2pk(id: PeerId) -> Result<secp256k1::PublicKey, secp256k1::Error> {
104 let mut s = [0u8; secp256k1::constants::UNCOMPRESSED_PUBLIC_KEY_SIZE];
107 s[0] = SECP256K1_TAG_PUBKEY_UNCOMPRESSED;
108 s[1..].copy_from_slice(id.as_slice());
109 secp256k1::PublicKey::from_slice(&s)
110}
111
112#[derive(
114 Debug, Clone, Eq, PartialEq, Hash, serde_with::SerializeDisplay, serde_with::DeserializeFromStr,
115)]
116pub enum AnyNode {
117 NodeRecord(NodeRecord),
119 #[cfg(feature = "secp256k1")]
121 Enr(Enr<secp256k1::SecretKey>),
122 PeerId(PeerId),
124 TrustedPeer(TrustedPeer),
126}
127
128impl AnyNode {
129 #[cfg(not(feature = "secp256k1"))]
131 pub const fn peer_id(&self) -> PeerId {
132 match self {
133 Self::NodeRecord(record) => record.id,
134 Self::PeerId(peer_id) => *peer_id,
135 Self::TrustedPeer(peer) => peer.id,
136 }
137 }
138
139 #[cfg(feature = "secp256k1")]
141 pub fn peer_id(&self) -> PeerId {
142 match self {
143 Self::NodeRecord(record) => record.id,
144 Self::Enr(enr) => pk2id(&enr.public_key()),
145 Self::PeerId(peer_id) => *peer_id,
146 Self::TrustedPeer(peer) => peer.id,
147 }
148 }
149
150 #[cfg(not(feature = "secp256k1"))]
152 pub const fn node_record(&self) -> Option<NodeRecord> {
153 match self {
154 Self::NodeRecord(record) => Some(*record),
155 Self::PeerId(_) | Self::TrustedPeer(_) => None,
156 }
157 }
158
159 #[cfg(feature = "secp256k1")]
161 pub fn node_record(&self) -> Option<NodeRecord> {
162 match self {
163 Self::NodeRecord(record) => Some(*record),
164 Self::Enr(enr) => {
165 let node_record = NodeRecord {
166 address: enr
167 .ip4()
168 .map(core::net::IpAddr::from)
169 .or_else(|| enr.ip6().map(core::net::IpAddr::from))?,
170 tcp_port: enr.tcp4().or_else(|| enr.tcp6())?,
171 udp_port: enr.udp4().or_else(|| enr.udp6())?,
172 id: pk2id(&enr.public_key()),
173 }
174 .into_ipv4_mapped();
175 Some(node_record)
176 }
177 Self::PeerId(_) | Self::TrustedPeer(_) => None,
178 }
179 }
180
181 pub const fn trusted_peer(&self) -> Option<&TrustedPeer> {
183 match self {
184 Self::TrustedPeer(peer) => Some(peer),
185 _ => None,
186 }
187 }
188}
189
190impl From<NodeRecord> for AnyNode {
191 fn from(value: NodeRecord) -> Self {
192 Self::NodeRecord(value)
193 }
194}
195
196#[cfg(feature = "secp256k1")]
197impl From<Enr<secp256k1::SecretKey>> for AnyNode {
198 fn from(value: Enr<secp256k1::SecretKey>) -> Self {
199 Self::Enr(value)
200 }
201}
202
203impl FromStr for AnyNode {
204 type Err = String;
205
206 fn from_str(s: &str) -> Result<Self, Self::Err> {
207 if let Some(rem) = s.strip_prefix("enode://") {
208 if let Ok(record) = NodeRecord::from_str(s) {
209 return Ok(Self::NodeRecord(record))
210 }
211 if let Ok(trusted) = TrustedPeer::from_str(s) {
213 return Ok(Self::TrustedPeer(trusted))
214 }
215 if let Ok(peer_id) = PeerId::from_str(rem) {
217 return Ok(Self::PeerId(peer_id))
218 }
219 return Err(format!("invalid public key: {rem}"))
220 }
221 #[cfg(feature = "secp256k1")]
222 if s.starts_with("enr:") {
223 return Enr::from_str(s).map(AnyNode::Enr)
224 }
225 Err("missing 'enr:' prefix for base64-encoded record".to_string())
226 }
227}
228
229impl core::fmt::Display for AnyNode {
230 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
231 match self {
232 Self::NodeRecord(record) => write!(f, "{record}"),
233 #[cfg(feature = "secp256k1")]
234 Self::Enr(enr) => write!(f, "{enr}"),
235 Self::PeerId(peer_id) => {
236 write!(f, "enode://{}", alloy_primitives::hex::encode(peer_id.as_slice()))
237 }
238 Self::TrustedPeer(peer) => write!(f, "{peer}"),
239 }
240 }
241}
242
243#[derive(Debug)]
245pub struct WithPeerId<T>(PeerId, pub T);
246
247impl<T> From<(PeerId, T)> for WithPeerId<T> {
248 fn from(value: (PeerId, T)) -> Self {
249 Self(value.0, value.1)
250 }
251}
252
253impl<T> WithPeerId<T> {
254 pub const fn new(peer: PeerId, value: T) -> Self {
256 Self(peer, value)
257 }
258
259 pub const fn peer_id(&self) -> PeerId {
261 self.0
262 }
263
264 pub const fn data(&self) -> &T {
266 &self.1
267 }
268
269 pub fn into_data(self) -> T {
271 self.1
272 }
273
274 pub fn transform<F: From<T>>(self) -> WithPeerId<F> {
276 WithPeerId(self.0, self.1.into())
277 }
278
279 pub fn split(self) -> (PeerId, T) {
281 (self.0, self.1)
282 }
283
284 pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> WithPeerId<U> {
286 WithPeerId(self.0, op(self.1))
287 }
288}
289
290impl<T> WithPeerId<Option<T>> {
291 pub fn transpose(self) -> Option<WithPeerId<T>> {
293 self.1.map(|v| WithPeerId(self.0, v))
294 }
295
296 pub fn unwrap(self) -> T {
304 self.1.unwrap()
305 }
306
307 pub fn unwrapped(self) -> WithPeerId<T> {
313 self.transpose().unwrap()
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320
321 #[cfg(feature = "secp256k1")]
322 #[test]
323 fn test_node_record_parse() {
324 let url = "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@10.3.58.6:30303?discport=30301";
325 let node: AnyNode = url.parse().unwrap();
326 assert_eq!(node, AnyNode::NodeRecord(NodeRecord {
327 address: std::net::IpAddr::V4([10,3,58,6].into()),
328 tcp_port: 30303,
329 udp_port: 30301,
330 id: "6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0".parse().unwrap(),
331 }));
332 assert_eq!(node.to_string(), url)
333 }
334
335 #[test]
336 fn test_peer_id_parse() {
337 let url = "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0";
338 let node: AnyNode = url.parse().unwrap();
339 assert_eq!(node, AnyNode::PeerId("6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0".parse().unwrap()));
340 assert_eq!(node.to_string(), url);
341
342 let url = "enode://";
343 let err = url.parse::<AnyNode>().unwrap_err();
344 assert_eq!(err, "invalid public key: ");
345 }
346
347 #[cfg(feature = "secp256k1")]
349 #[test]
350 fn test_enr_parse() {
351 let url = "enr:-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnh4MHcBFZntXNFrdvJjX04jRzjzCBOonrkTfj499SZuOh8R33Ls8RRcy5wBgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8yWM0xOIN1ZHCCdl8";
352 let node: AnyNode = url.parse().unwrap();
353 assert_eq!(
354 node.peer_id(),
355 "0xca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f"
356 .parse::<PeerId>()
357 .unwrap()
358 );
359 assert_eq!(node.to_string(), url);
360 }
361
362 #[test]
363 fn test_trusted_peer_parse_hostname() {
364 let url = "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@my-node.example.com:30303";
365 let node: AnyNode = url.parse().unwrap();
366 assert!(matches!(node, AnyNode::TrustedPeer(_)));
367 assert_eq!(
368 node.peer_id(),
369 "6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0".parse::<PeerId>().unwrap()
370 );
371 assert!(node.node_record().is_none());
372 assert!(node.trusted_peer().is_some());
373 assert_eq!(node.to_string(), url);
374 }
375
376 #[test]
377 #[cfg(feature = "secp256k1")]
378 fn pk2id2pk() {
379 let prikey = secp256k1::SecretKey::new(&mut rand_08::thread_rng());
380 let pubkey = secp256k1::PublicKey::from_secret_key(secp256k1::SECP256K1, &prikey);
381 assert_eq!(pubkey, id2pk(pk2id(&pubkey)).unwrap());
382 }
383}