discv5/
node_info.rs

1use super::*;
2use crate::Enr;
3use enr::{CombinedPublicKey, NodeId};
4use std::net::SocketAddr;
5
6#[cfg(feature = "libp2p")]
7use libp2p_identity::{KeyType, PublicKey};
8#[cfg(feature = "libp2p")]
9use multiaddr::{Multiaddr, Protocol};
10
11/// This type relaxes the requirement of having an ENR to connect to a node, to allow for unsigned
12/// connection types, such as multiaddrs.
13#[derive(Debug, Clone, PartialEq)]
14pub struct NodeContact {
15    /// Key to use for communications with this node.
16    public_key: CombinedPublicKey,
17    /// Address to use to contact the node.
18    socket_addr: SocketAddr,
19    /// The ENR of the node if known.
20    enr: Option<Enr>,
21}
22
23#[derive(Debug, Clone)]
24pub struct NonContactable {
25    pub enr: Enr,
26}
27
28impl NodeContact {
29    pub fn new(public_key: CombinedPublicKey, socket_addr: SocketAddr, enr: Option<Enr>) -> Self {
30        NodeContact {
31            public_key,
32            socket_addr,
33            enr,
34        }
35    }
36
37    pub fn node_id(&self) -> NodeId {
38        match self.enr {
39            Some(ref enr) => enr.node_id(),
40            None => self.public_key.clone().into(),
41        }
42    }
43
44    pub fn seq_no(&self) -> Option<u64> {
45        self.enr.as_ref().map(|enr| enr.seq())
46    }
47
48    pub fn public_key(&self) -> CombinedPublicKey {
49        self.public_key.clone()
50    }
51
52    pub fn enr(&self) -> Option<Enr> {
53        self.enr.clone()
54    }
55
56    pub fn socket_addr(&self) -> SocketAddr {
57        self.socket_addr
58    }
59
60    pub fn node_address(&self) -> NodeAddress {
61        NodeAddress {
62            socket_addr: self.socket_addr,
63            node_id: self.node_id(),
64        }
65    }
66
67    pub fn to_address_and_enr(self) -> (NodeAddress, Option<Enr>) {
68        let NodeContact {
69            public_key,
70            socket_addr,
71            enr,
72        } = self;
73        (
74            NodeAddress {
75                node_id: public_key.into(),
76                socket_addr,
77            },
78            enr,
79        )
80    }
81
82    pub fn try_from_enr(enr: Enr, ip_mode: IpMode) -> Result<Self, NonContactable> {
83        let socket_addr = match ip_mode.get_contactable_addr(&enr) {
84            Some(socket_addr) => socket_addr,
85            None => return Err(NonContactable { enr }),
86        };
87
88        Ok(NodeContact {
89            public_key: enr.public_key(),
90            socket_addr,
91            enr: Some(enr),
92        })
93    }
94
95    #[cfg(feature = "libp2p")]
96    pub fn try_from_multiaddr(multiaddr: Multiaddr) -> Result<Self, &'static str> {
97        // The multiaddr must contain either the ip4 or ip6 protocols, the UDP protocol and the P2P
98        // protocol with either secp256k1 or ed25519 keys.
99
100        // perform a single pass and try to fill all required protocols from the multiaddr
101        let mut ip_addr = None;
102        let mut udp_port = None;
103        let mut p2p = None;
104
105        for protocol in multiaddr.into_iter() {
106            match protocol {
107                Protocol::Udp(port) => udp_port = Some(port),
108                Protocol::Ip4(addr) => ip_addr = Some(addr.into()),
109                Protocol::Ip6(addr) => ip_addr = Some(addr.into()),
110                Protocol::P2p(peer_id) => p2p = Some(peer_id),
111                _ => {}
112            }
113        }
114
115        let udp_port = udp_port.ok_or("A UDP port must be specified in the multiaddr")?;
116        let ip_addr = ip_addr.ok_or("An IP address must be specified in the multiaddr")?;
117        let peer_id = p2p.ok_or("The p2p protocol must be specified in the multiaddr")?;
118
119        let public_key: CombinedPublicKey = {
120            let pk = PublicKey::try_decode_protobuf(&peer_id.to_bytes()[2..])
121                .map_err(|_| "Invalid public key")?;
122            match pk.key_type() {
123                KeyType::Secp256k1 => enr::k256::ecdsa::VerifyingKey::from_sec1_bytes(
124                    &pk.try_into_secp256k1()
125                        .expect("Must be secp256k1")
126                        .to_bytes_uncompressed(),
127                )
128                .expect("Libp2p key conversion, always valid")
129                .into(),
130                KeyType::Ed25519 => enr::ed25519_dalek::VerifyingKey::from_bytes(
131                    &pk.try_into_ed25519().expect("Must be ed25519").to_bytes(),
132                )
133                .expect("Libp2p key conversion, always valid")
134                .into(),
135                _ => return Err("The key type is not supported"),
136            }
137        };
138
139        Ok(NodeContact {
140            public_key,
141            socket_addr: SocketAddr::new(ip_addr, udp_port),
142            enr: None,
143        })
144    }
145}
146
147#[cfg(test)]
148impl From<Enr> for NodeContact {
149    #[track_caller]
150    fn from(enr: Enr) -> Self {
151        NodeContact::try_from_enr(enr, IpMode::default()).unwrap()
152    }
153}
154
155impl std::fmt::Display for NodeContact {
156    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157        write!(f, "Node: {}, addr: {:?}", self.node_id(), self.socket_addr)
158    }
159}
160
161/// A representation of an unsigned contactable node.
162#[derive(PartialEq, Hash, Eq, Clone, Debug)]
163pub struct NodeAddress {
164    /// The destination socket address.
165    pub socket_addr: SocketAddr,
166    /// The destination Node Id.
167    pub node_id: NodeId,
168}
169
170impl Ord for NodeAddress {
171    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
172        let ord = self.node_id.raw().cmp(&other.node_id.raw());
173        if ord != std::cmp::Ordering::Equal {
174            return ord;
175        }
176        let ord = self.socket_addr.ip().cmp(&other.socket_addr.ip());
177        if ord != std::cmp::Ordering::Equal {
178            return ord;
179        }
180        self.socket_addr.port().cmp(&other.socket_addr.port())
181    }
182}
183
184impl PartialOrd for NodeAddress {
185    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
186        Some(self.cmp(other))
187    }
188}
189
190impl NodeAddress {
191    pub fn new(socket_addr: SocketAddr, node_id: NodeId) -> Self {
192        Self {
193            socket_addr,
194            node_id,
195        }
196    }
197}
198
199impl std::fmt::Display for NodeAddress {
200    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201        write!(f, "Node: {}, addr: {:?}", self.node_id, self.socket_addr)
202    }
203}