Skip to main content

p2panda_net/
utils.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Formatting, conversion and connectivity status utilities.
4use std::net::{IpAddr, SocketAddr};
5
6use p2panda_core::Topic;
7
8use crate::NodeId;
9
10/// Returns a displayable string representing the underlying value in a short format, easy to read
11/// during debugging and logging.
12pub trait ShortFormat {
13    fn fmt_short(&self) -> String;
14}
15
16impl ShortFormat for NodeId {
17    fn fmt_short(&self) -> String {
18        self.to_hex()[0..10].to_string()
19    }
20}
21
22impl ShortFormat for iroh_base::EndpointId {
23    fn fmt_short(&self) -> String {
24        self.to_string()[0..10].to_string()
25    }
26}
27
28impl ShortFormat for [u8; 32] {
29    fn fmt_short(&self) -> String {
30        hex::encode(&self[0..5]).to_string()
31    }
32}
33
34impl ShortFormat for Topic {
35    fn fmt_short(&self) -> String {
36        self.to_string()[0..10].to_string()
37    }
38}
39
40impl ShortFormat for Vec<u8> {
41    fn fmt_short(&self) -> String {
42        hex::encode(&self[0..5]).to_string()
43    }
44}
45
46impl ShortFormat for Vec<NodeId> {
47    fn fmt_short(&self) -> String {
48        let list: Vec<String> = self.iter().map(|addr| addr.fmt_short()).collect();
49        format!("[{}]", list.join(", "))
50    }
51}
52
53impl ShortFormat for Vec<iroh_base::EndpointId> {
54    fn fmt_short(&self) -> String {
55        let list: Vec<String> = self
56            .iter()
57            .map(|addr| addr.fmt_short().to_string())
58            .collect();
59        format!("[{}]", list.join(", "))
60    }
61}
62
63/// Connectivity status derived from a given IP address.
64///
65/// Defines whether a node appears to be locally-reachable (on the host machine or via WLAN) or
66/// globally-reachable (via the internet). This is not a guarantee of the overall node connectivity
67/// status but a best-guess based on the provided IP address.
68#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
69pub enum ConnectivityStatus {
70    /// The IP address is neither link-local, loopback nor global.
71    Other,
72
73    /// The IP address is link-local or loopback.
74    Local,
75
76    /// The IP address appears to be globally reachable.
77    Global,
78}
79
80impl ConnectivityStatus {
81    pub fn is_global(&self) -> bool {
82        self == &ConnectivityStatus::Global
83    }
84}
85
86/// Parse a `SocketAddr` and return the best approximation of the connectivity status based on the
87/// IP address.
88pub fn connectivity_status(addr: &SocketAddr) -> ConnectivityStatus {
89    match addr.ip() {
90        IpAddr::V4(ip) => {
91            if ip.is_loopback() || ip.is_link_local() || ip.is_private() {
92                ConnectivityStatus::Local
93            } else if ip.is_multicast()
94                || ip.is_broadcast()
95                || ip.is_documentation()
96                || ip.is_unspecified()
97            {
98                ConnectivityStatus::Other
99            } else {
100                ConnectivityStatus::Global
101            }
102        }
103        IpAddr::V6(ip) => {
104            if ip.is_loopback() || ip.is_unique_local() || ip.is_unicast_link_local() {
105                ConnectivityStatus::Local
106            } else if ip.is_multicast() || ip.is_unspecified() {
107                ConnectivityStatus::Other
108            } else {
109                ConnectivityStatus::Global
110            }
111        }
112    }
113}
114
115/// Converts an `iroh` public key type to the `p2panda-core` implementation.
116pub fn to_verifying_key(key: iroh_base::PublicKey) -> p2panda_core::VerifyingKey {
117    p2panda_core::VerifyingKey::from_bytes(key.as_bytes()).expect("already validated public key")
118}
119
120/// Converts a `p2panda-core` verifying key to the "iroh" type.
121pub fn from_verifying_key(key: p2panda_core::VerifyingKey) -> iroh_base::PublicKey {
122    iroh_base::PublicKey::from_bytes(key.as_bytes()).expect("already validated public key")
123}
124
125/// Converts a `p2panda-core` signing key to the "iroh" type.
126pub fn from_signing_key(key: p2panda_core::SigningKey) -> iroh_base::SecretKey {
127    iroh_base::SecretKey::from_bytes(key.as_bytes())
128}
129
130#[cfg(test)]
131mod tests {
132    use super::ConnectivityStatus;
133
134    #[test]
135    fn order() {
136        assert!(ConnectivityStatus::Global > ConnectivityStatus::Local);
137        assert!(ConnectivityStatus::Local > ConnectivityStatus::Other);
138    }
139}