Skip to main content

ethrex_p2p/
types.rs

1use bytes::{BufMut, Bytes};
2use ethrex_common::types::ForkId;
3use ethrex_common::{H256, H264, H512};
4use ethrex_crypto::keccak::keccak_hash;
5use ethrex_rlp::{
6    decode::RLPDecode,
7    encode::RLPEncode,
8    error::RLPDecodeError,
9    structs::{self, Decoder, Encoder},
10};
11use secp256k1::{PublicKey, SecretKey, ecdsa::Signature};
12use serde::{Deserialize, Serialize, ser::Serializer};
13use std::net::Ipv6Addr;
14use std::{
15    fmt::Display,
16    net::{IpAddr, Ipv4Addr, SocketAddr},
17    str::FromStr,
18    sync::OnceLock,
19};
20use thiserror::Error;
21
22use crate::utils::node_id;
23
24/// Holds the local node's network addressing configuration, separating the
25/// socket bind address from the externally-announced address.
26///
27/// This is relevant for nodes running behind NAT: they bind to a private
28/// address (e.g. `0.0.0.0`) but must announce their public IP to peers.
29#[derive(Debug, Clone)]
30pub struct NetworkConfig {
31    /// Address to bind UDP/TCP sockets to (e.g. `0.0.0.0` or `::`)
32    pub bind_addr: IpAddr,
33    pub tcp_port: u16,
34    pub udp_port: u16,
35}
36
37impl NetworkConfig {
38    /// Returns the socket address to bind the TCP listener to.
39    pub fn bind_tcp_addr(&self) -> SocketAddr {
40        SocketAddr::new(self.bind_addr, self.tcp_port)
41    }
42
43    /// Returns the socket address to bind the UDP socket to.
44    pub fn bind_udp_addr(&self) -> SocketAddr {
45        SocketAddr::new(self.bind_addr, self.udp_port)
46    }
47
48    /// Builds a `NetworkConfig` where bind and external addresses are both
49    /// taken from `node`. Useful when no NAT mapping is needed.
50    pub fn from_node(node: &Node) -> Self {
51        Self {
52            bind_addr: node.ip,
53            tcp_port: node.tcp_port,
54            udp_port: node.udp_port,
55        }
56    }
57}
58
59#[derive(Debug, Error)]
60pub enum NodeError {
61    #[error("Invalid format: {0}")]
62    InvalidFormat(String),
63    #[error("Parse error: {0}")]
64    ParseError(String),
65    #[error("RLP decode error: {0}")]
66    RLPDecodeError(#[from] RLPDecodeError),
67    #[error("Missing field: {0}")]
68    MissingField(String),
69    #[error("Signature error: {0}")]
70    SignatureError(String),
71}
72
73const MAX_NODE_RECORD_ENCODED_SIZE: usize = 300;
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub struct Endpoint {
77    pub ip: IpAddr,
78    pub udp_port: u16,
79    pub tcp_port: u16,
80}
81
82impl RLPEncode for Endpoint {
83    fn encode(&self, buf: &mut dyn BufMut) {
84        Encoder::new(buf)
85            .encode_field(&self.ip)
86            .encode_field(&self.udp_port)
87            .encode_field(&self.tcp_port)
88            .finish();
89    }
90}
91
92impl RLPDecode for Endpoint {
93    fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
94        let decoder = Decoder::new(rlp)?;
95        let (ip, decoder) = decoder.decode_field("ip")?;
96        let (udp_port, decoder) = decoder.decode_field("udp_port")?;
97        let (tcp_port, decoder) = decoder.decode_field("tcp_port")?;
98        let remaining = decoder.finish()?;
99        let endpoint = Endpoint {
100            ip,
101            udp_port,
102            tcp_port,
103        };
104        Ok((endpoint, remaining))
105    }
106}
107
108#[derive(Debug, Clone, PartialEq, Eq)]
109pub struct Node {
110    pub ip: IpAddr,
111    pub udp_port: u16,
112    pub tcp_port: u16,
113    pub public_key: H512,
114    pub version: Option<String>,
115    node_id: OnceLock<H256>,
116}
117
118impl RLPDecode for Node {
119    fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
120        let decoder = Decoder::new(rlp)?;
121        let (ip, decoder) = decoder.decode_field("ip")?;
122        let (udp_port, decoder) = decoder.decode_field("upd_port")?;
123        let (tcp_port, decoder) = decoder.decode_field("tcp_port")?;
124        let (public_key, decoder) = decoder.decode_field("public_key")?;
125        let remaining = decoder.finish_unchecked();
126
127        let node = Node::new(ip, udp_port, tcp_port, public_key);
128        Ok((node, remaining))
129    }
130}
131
132impl<'de> serde::de::Deserialize<'de> for Node {
133    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
134    where
135        D: serde::Deserializer<'de>,
136    {
137        Node::from_str(&<String>::deserialize(deserializer)?)
138            .map_err(|e| serde::de::Error::custom(format!("{}", e)))
139    }
140}
141
142impl serde::Serialize for Node {
143    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
144    where
145        S: Serializer,
146    {
147        serializer.serialize_str(&self.enode_url())
148    }
149}
150
151impl FromStr for Node {
152    type Err = NodeError;
153
154    fn from_str(s: &str) -> Result<Self, Self::Err> {
155        match s {
156            s if s.starts_with("enode://") => Self::from_enode_url(s),
157            s if s.starts_with("enr:") => Self::from_enr_url(s),
158            _ => Err(NodeError::InvalidFormat(
159                "Invalid network address format".into(),
160            )),
161        }
162    }
163}
164
165impl Node {
166    pub fn new(ip: IpAddr, udp_port: u16, tcp_port: u16, public_key: H512) -> Self {
167        Self {
168            ip,
169            udp_port,
170            tcp_port,
171            public_key,
172            version: None,
173            node_id: OnceLock::new(),
174        }
175    }
176
177    pub fn client_name(&self) -> &str {
178        self.version
179            .as_deref()
180            .and_then(|version| {
181                let base = version
182                    .split_once('/')
183                    .map(|(name, _)| name.trim())
184                    .unwrap_or_else(|| version.trim());
185                if base.is_empty() { None } else { Some(base) }
186            })
187            .unwrap_or("unknown")
188    }
189
190    pub fn from_enode_url(enode: &str) -> Result<Self, NodeError> {
191        let public_key = H512::from_str(&enode[8..136])
192            .map_err(|_| NodeError::ParseError("Could not parse public_key".into()))?;
193
194        let address_start = 137;
195        let address_part = &enode[address_start..];
196
197        // Remove `?discport=` if present
198        let address_part = match address_part.find('?') {
199            Some(pos) => &address_part[..pos],
200            None => address_part,
201        };
202
203        let socket_address: SocketAddr = address_part
204            .parse()
205            .map_err(|_| NodeError::ParseError("Could not parse socket address".into()))?;
206        let ip = socket_address.ip();
207        let port = socket_address.port();
208
209        let udp_port = match enode.find("?discport=") {
210            Some(pos) => enode[pos + 10..]
211                .parse()
212                .map_err(|_| NodeError::ParseError("Could not parse discport".into()))?,
213            None => port,
214        };
215
216        Ok(Self::new(ip, udp_port, port, public_key))
217    }
218
219    pub fn from_enr_url(enr: &str) -> Result<Self, NodeError> {
220        let base64_decoded = ethrex_common::base64::decode(&enr.as_bytes()[4..]);
221        let record = NodeRecord::decode(&base64_decoded).map_err(NodeError::from)?;
222        Node::from_enr(&record)
223    }
224
225    pub fn from_enr(record: &NodeRecord) -> Result<Self, NodeError> {
226        let pairs = record.pairs();
227        let public_key = pairs.secp256k1.ok_or(NodeError::MissingField(
228            "public key not found in record".into(),
229        ))?;
230        let verifying_key = PublicKey::from_slice(public_key.as_bytes()).map_err(|_| {
231            NodeError::ParseError("public key could not be built from msg pub key bytes".into())
232        })?;
233        let encoded = verifying_key.serialize_uncompressed();
234        let public_key = H512::from_slice(&encoded[1..]);
235
236        let ip: IpAddr = match (pairs.ip, pairs.ip6) {
237            (None, None) => {
238                return Err(NodeError::MissingField(
239                    "Ip not found in record, can't construct node".into(),
240                ));
241            }
242            (None, Some(ipv6)) => IpAddr::from(ipv6),
243            (Some(ipv4), None) => IpAddr::from(ipv4),
244            (Some(ipv4), Some(_ipv6)) => IpAddr::from(ipv4),
245        };
246
247        // both udp and tcp can be defined in the pairs or only one
248        // in the latter case, we have to default both ports to the one provided
249        let udp_port = pairs
250            .udp_port
251            .or(pairs.tcp_port)
252            .ok_or(NodeError::MissingField("No port found in record".into()))?;
253        let tcp_port = pairs
254            .tcp_port
255            .or(pairs.udp_port)
256            .ok_or(NodeError::MissingField("No port found in record".into()))?;
257
258        Ok(Self::new(ip, udp_port, tcp_port, public_key))
259    }
260
261    pub fn enode_url(&self) -> String {
262        let public_key = hex::encode(self.public_key);
263        let node_ip = self.ip;
264        let discovery_port = self.udp_port;
265        let listener_port = self.tcp_port;
266        if discovery_port != listener_port {
267            format!("enode://{public_key}@{node_ip}:{listener_port}?discport={discovery_port}")
268        } else {
269            format!("enode://{public_key}@{node_ip}:{listener_port}")
270        }
271    }
272
273    pub fn udp_addr(&self) -> SocketAddr {
274        // Nodes that use ipv6 currently are only ipv4 masked addresses, so we can convert it to an ipv4 address.
275        // If in the future we have real ipv6 nodes, we will need to handle them differently.
276        SocketAddr::new(self.ip.to_canonical(), self.udp_port)
277    }
278
279    pub fn tcp_addr(&self) -> SocketAddr {
280        SocketAddr::new(self.ip, self.tcp_port)
281    }
282
283    pub fn node_id(&self) -> H256 {
284        *self.node_id.get_or_init(|| node_id(&self.public_key))
285    }
286}
287
288impl Display for Node {
289    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290        f.write_str(&format!(
291            "{0} #{1}({2}:{3})",
292            self.client_name(),
293            self.node_id(),
294            self.ip,
295            self.tcp_port
296        ))
297    }
298}
299
300/// Reference: [ENR records](https://github.com/ethereum/devp2p/blob/master/enr.md)
301#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize)]
302pub struct NodeRecordPairs {
303    /// The ID of the identity scheme: https://github.com/ethereum/devp2p/blob/master/enr.md#v4-identity-scheme
304    /// This is always "v4".
305    pub id: Option<String>,
306    pub ip: Option<Ipv4Addr>,
307    pub ip6: Option<Ipv6Addr>,
308    // the record structure reference says that tcp_port and udp_ports are big-endian integers
309    // but they are actually encoded as 2 bytes, see geth for example: https://github.com/ethereum/go-ethereum/blob/f544fc3b4659aeca24a6de83f820dd61ea9b39db/p2p/enr/entries.go#L60-L78
310    // I think the confusion comes from the fact that geth decodes the bytes and then builds an IPV4/6 big-integer structure.
311    pub tcp_port: Option<u16>,
312    pub udp_port: Option<u16>,
313    pub secp256k1: Option<H264>,
314    // https://github.com/ethereum/devp2p/blob/master/enr-entries/eth.md
315    pub eth: Option<ForkId>,
316    // Snap entry is being used by some tests such as `test_encode_enr_response`.
317    pub snap: Option<Vec<u32>>,
318    pub other: Vec<(Bytes, Bytes)>,
319    // TODO implement ipv6 specific ports
320}
321
322impl NodeRecordPairs {
323    pub fn try_from_raw_pairs(
324        pairs: Vec<(Bytes, Bytes)>,
325    ) -> Result<NodeRecordPairs, RLPDecodeError> {
326        let mut decoded_pairs = NodeRecordPairs::default();
327        for (key, value) in pairs {
328            match key.as_ref() {
329                b"id" => decoded_pairs.id = Some(String::decode(&value)?),
330                b"ip" => decoded_pairs.ip = Some(Ipv4Addr::decode(&value)?),
331                b"ip6" => decoded_pairs.ip6 = Some(Ipv6Addr::decode(&value)?),
332                b"tcp" => decoded_pairs.tcp_port = Some(u16::decode(&value)?),
333                b"udp" => decoded_pairs.udp_port = Some(u16::decode(&value)?),
334                b"secp256k1" => decoded_pairs.secp256k1 = Some(H264(<[u8; 33]>::decode(&value)?)),
335                b"snap" => decoded_pairs.snap = Some(Vec::<u32>::decode(&value)?),
336                b"eth" => {
337                    // https://github.com/ethereum/devp2p/blob/master/enr-entries/eth.md
338                    // entry-value = [[ forkHash, forkNext ], ...]
339                    let decoder = Decoder::new(&value)?;
340                    // Here we decode fork-id = [ forkHash, forkNext ]
341                    let (fork_id, decoder) = decoder.decode_field("forkId")?;
342
343                    // As per the spec, we should ignore any additional list elements in entry-value
344                    decoder.finish_unchecked();
345                    decoded_pairs.eth = Some(fork_id);
346                }
347                // Key is some random bytes sequence which we don't care
348                _ => {
349                    decoded_pairs.other.push((key, value));
350                }
351            }
352        }
353
354        Ok(decoded_pairs)
355    }
356
357    /// Encodes to a list of (key, value) where keys are ascii bytes and values are rlp encoded bytes.
358    pub fn encode_pairs(&self) -> Vec<(Bytes, Bytes)> {
359        // The key/value pairs must be sorted by key and must be unique
360        let mut pairs = vec![];
361        if let Some(eth) = &self.eth {
362            // Without the Vec wrapper, RLP encoding fork_id directly would produce:
363            // [forkHash, forkNext]
364            // But the spec requires nested lists:
365            // [[forkHash, forkNext]]
366            let eth = vec![eth.clone()];
367            pairs.push(("eth".into(), eth.encode_to_vec().into()));
368        }
369        if let Some(id) = self.id.as_ref() {
370            pairs.push(("id".into(), id.encode_to_vec().into()));
371        }
372        if let Some(ip) = self.ip {
373            pairs.push(("ip".into(), ip.encode_to_vec().into()));
374        }
375        if let Some(ip6) = self.ip6 {
376            pairs.push(("ip6".into(), ip6.encode_to_vec().into()));
377        }
378        if let Some(secp256k1) = self.secp256k1 {
379            pairs.push(("secp256k1".into(), secp256k1.encode_to_vec().into()));
380        }
381        if let Some(snap) = self.snap.as_ref() {
382            pairs.push(("snap".into(), snap.encode_to_vec().into()));
383        }
384
385        if let Some(tcp) = self.tcp_port {
386            pairs.push(("tcp".into(), tcp.encode_to_vec().into()));
387        }
388        if let Some(udp) = self.udp_port {
389            pairs.push(("udp".into(), udp.encode_to_vec().into()));
390        }
391        pairs.extend(self.other.clone());
392        pairs.sort_by(|(left_key, _), (right_key, _)| left_key.cmp(right_key));
393        pairs
394    }
395}
396
397pub const INITIAL_ENR_SEQ: u64 = 1;
398
399/// Reference: [ENR records](https://github.com/ethereum/devp2p/blob/master/enr.md#record-structure)
400#[derive(Debug, PartialEq, Clone, Eq, Default, Serialize, Deserialize)]
401pub struct NodeRecord {
402    pub signature: H512,
403    pub seq: u64,
404    /// The remainder of the record consists of key/value pairs represented as NodeRecordPairs
405    pairs: NodeRecordPairs,
406}
407
408impl NodeRecord {
409    pub fn new(signature: H512, seq: u64, pairs: NodeRecordPairs) -> Self {
410        Self {
411            signature,
412            seq,
413            pairs,
414        }
415    }
416
417    pub fn enr_url(&self) -> Result<String, NodeError> {
418        let rlp_encoded = self.encode_to_vec();
419        let base64_encoded = ethrex_common::base64::encode(&rlp_encoded);
420        let mut result: String = "enr:".into();
421        let base64_encoded = String::from_utf8(base64_encoded)
422            .map_err(|_| NodeError::ParseError("Could not base 64 encode enr record".into()))?;
423        result.push_str(&base64_encoded);
424        Ok(result)
425    }
426
427    pub fn from_node(node: &Node, seq: u64, signer: &SecretKey) -> Result<Self, NodeError> {
428        let mut pairs = NodeRecordPairs {
429            id: Some("v4".to_string()),
430            secp256k1: Some(H264::from_slice(
431                &PublicKey::from_secret_key(secp256k1::SECP256K1, signer).serialize(),
432            )),
433            tcp_port: Some(node.tcp_port),
434            udp_port: Some(node.udp_port),
435            ..Default::default()
436        };
437        match node.ip.to_canonical() {
438            IpAddr::V4(ip) => pairs.ip = Some(ip),
439            IpAddr::V6(ip) => pairs.ip6 = Some(ip),
440        }
441
442        let mut record = NodeRecord {
443            seq,
444            pairs,
445            ..Default::default()
446        };
447        record.signature = record.sign_record(signer)?;
448
449        Ok(record)
450    }
451
452    pub fn set_fork_id(&mut self, fork_id: ForkId, signer: &SecretKey) -> Result<(), NodeError> {
453        self.pairs.eth = Some(fork_id);
454        self.update(signer)
455    }
456
457    pub fn get_fork_id(&self) -> Option<&ForkId> {
458        self.pairs.eth.as_ref()
459    }
460
461    fn update(&mut self, signer: &SecretKey) -> Result<(), NodeError> {
462        self.seq += 1;
463        self.signature = self.sign_record(signer)?;
464        Ok(())
465    }
466
467    pub fn sign_record(&self, signer: &SecretKey) -> Result<H512, NodeError> {
468        let digest = &self.get_signature_digest();
469        let msg = secp256k1::Message::from_digest_slice(digest)
470            .map_err(|_| NodeError::SignatureError("Invalid message digest".into()))?;
471        let (_recovery_id, signature_bytes) = secp256k1::SECP256K1
472            .sign_ecdsa_recoverable(&msg, signer)
473            .serialize_compact();
474
475        Ok(H512::from_slice(&signature_bytes))
476    }
477
478    pub fn get_signature_digest(&self) -> [u8; 32] {
479        let mut rlp = vec![];
480        structs::Encoder::new(&mut rlp)
481            .encode_field(&self.seq)
482            .encode_key_value_list::<Bytes>(&self.pairs.encode_pairs())
483            .finish();
484        keccak_hash(&rlp)
485    }
486
487    /// Verifies the ENR signature using the embedded public key.
488    /// Returns true if the signature is valid, false otherwise.
489    pub fn verify_signature(&self) -> bool {
490        let pairs = self.pairs();
491        let Some(pubkey_bytes) = pairs.secp256k1 else {
492            return false;
493        };
494
495        let Ok(pubkey) = PublicKey::from_slice(pubkey_bytes.as_bytes()) else {
496            return false;
497        };
498
499        let digest = self.get_signature_digest();
500        let Ok(message) = secp256k1::Message::from_digest_slice(&digest) else {
501            return false;
502        };
503
504        let Ok(signature) = Signature::from_compact(self.signature.as_bytes()) else {
505            return false;
506        };
507
508        secp256k1::SECP256K1
509            .verify_ecdsa(&message, &signature, &pubkey)
510            .is_ok()
511    }
512
513    pub fn pairs(&self) -> &NodeRecordPairs {
514        &self.pairs
515    }
516}
517
518impl RLPDecode for NodeRecord {
519    fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
520        let decoder = Decoder::new(rlp)?;
521        if decoder.get_payload_len() > MAX_NODE_RECORD_ENCODED_SIZE {
522            return Err(RLPDecodeError::InvalidLength);
523        }
524        let (signature, decoder) = decoder.decode_field("signature")?;
525        let (seq, decoder) = decoder.decode_field("seq")?;
526        let (pairs, decoder) = decode_node_record_optional_fields(vec![], decoder)?;
527
528        // all fields in pairs are optional except for id
529        let id_pair = pairs.iter().find(|(k, _v)| k.eq("id".as_bytes()));
530        if id_pair.is_some() {
531            let pairs = NodeRecordPairs::try_from_raw_pairs(pairs)?;
532            let node_record = NodeRecord {
533                signature,
534                seq,
535                pairs,
536            };
537            let remaining = decoder.finish()?;
538            Ok((node_record, remaining))
539        } else {
540            Err(RLPDecodeError::Custom(
541                "Invalid node record, 'id' field missing".into(),
542            ))
543        }
544    }
545}
546
547/// The NodeRecord optional fields are encoded as key/value pairs, according to the documentation
548/// <https://github.com/ethereum/devp2p/blob/master/enr.md#record-structure>
549/// This function returns a vector with (key, value) tuples. Both keys and values are stored as Bytes.
550/// Each value is the actual RLP encoding of the field including its prefix so it can be decoded as T::decode(value)
551fn decode_node_record_optional_fields(
552    mut pairs: Vec<(Bytes, Bytes)>,
553    decoder: Decoder,
554) -> Result<(Vec<(Bytes, Bytes)>, Decoder), RLPDecodeError> {
555    let (key, decoder): (Option<Bytes>, Decoder) = decoder.decode_optional_field();
556    if let Some(k) = key {
557        let (value, decoder): (Vec<u8>, Decoder) = decoder.get_encoded_item()?;
558        pairs.push((k, Bytes::from(value)));
559        decode_node_record_optional_fields(pairs, decoder)
560    } else {
561        Ok((pairs, decoder))
562    }
563}
564
565impl RLPEncode for NodeRecord {
566    fn encode(&self, buf: &mut dyn BufMut) {
567        structs::Encoder::new(buf)
568            .encode_field(&self.signature)
569            .encode_field(&self.seq)
570            .encode_key_value_list::<Bytes>(&self.pairs.encode_pairs())
571            .finish();
572    }
573}
574
575impl RLPEncode for Node {
576    fn encode(&self, buf: &mut dyn BufMut) {
577        structs::Encoder::new(buf)
578            .encode_field(&self.ip)
579            .encode_field(&self.udp_port)
580            .encode_field(&self.tcp_port)
581            .encode_field(&self.public_key)
582            .finish();
583    }
584}