Skip to main content

zebra_rpc/methods/types/
peer_info.rs

1//! An array of [`PeerInfo`] is the output of the `getpeerinfo` RPC method.
2
3use derive_getters::Getters;
4use derive_new::new;
5use zebra_network::{types::MetaAddr, PeerSocketAddr};
6
7/// Item of the `getpeerinfo` response
8#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
9#[allow(clippy::too_many_arguments)]
10pub struct PeerInfo {
11    /// The IP address and port of the peer
12    #[getter(copy)]
13    pub(crate) addr: PeerSocketAddr,
14
15    /// The services offered, as a zero-padded 16-character hex string
16    pub(crate) services: String,
17
18    /// The time in seconds since epoch of the last message received from this peer
19    pub(crate) lastrecv: u32,
20
21    /// Inbound (true) or Outbound (false)
22    pub(crate) inbound: bool,
23
24    /// The ban score for misbehavior
25    pub(crate) banscore: u32,
26
27    /// The peer's user agent string (e.g. "/Zebra:2.1.0/" or "/MagicBean:5.8.0/")
28    pub(crate) subver: String,
29
30    /// The negotiated protocol version
31    pub(crate) version: u32,
32
33    /// The connection state (e.g. "connected", "never_connected", "failed", "connecting")
34    pub(crate) connection_state: String,
35
36    /// The round-trip ping time in seconds.
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub(crate) pingtime: Option<f64>,
39
40    /// The wait time on a ping response in seconds.
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub(crate) pingwait: Option<f64>,
43}
44
45/// Response type for the `getpeerinfo` RPC method.
46pub type GetPeerInfoResponse = Vec<PeerInfo>;
47
48impl PeerInfo {
49    /// Builds a [`PeerInfo`] from an address book entry and the misbehavior
50    /// score of its peer group.
51    ///
52    /// `banscore` comes from the address book rather than the [`MetaAddr`],
53    /// because misbehavior is tracked per peer group — one IPv4 address, or one
54    /// IPv6 `/64` subnet — not per individual address.
55    pub fn from_meta_addr(meta_addr: MetaAddr, banscore: u32) -> Self {
56        let services = meta_addr
57            .services()
58            .map(|s| format!("{:016x}", s.bits()))
59            .unwrap_or_else(|| "0000000000000000".to_string());
60
61        let lastrecv = meta_addr.last_seen().map(|t| t.timestamp()).unwrap_or(0);
62
63        let connection_state = meta_addr.last_connection_state().to_string();
64
65        let subver = meta_addr.user_agent().unwrap_or("").to_string();
66
67        let version = meta_addr.negotiated_version().map(|v| v.0).unwrap_or(0);
68
69        Self {
70            addr: meta_addr.addr(),
71            services,
72            lastrecv,
73            inbound: meta_addr.is_inbound(),
74            banscore,
75            subver,
76            version,
77            connection_state,
78            pingtime: meta_addr.rtt().map(|d| d.as_secs_f64()),
79            pingwait: meta_addr.ping_sent_at().map(|t| t.elapsed().as_secs_f64()),
80        }
81    }
82}
83
84impl Default for PeerInfo {
85    fn default() -> Self {
86        Self {
87            addr: PeerSocketAddr::unspecified(),
88            services: "0000000000000000".to_string(),
89            lastrecv: 0,
90            inbound: false,
91            banscore: 0,
92            subver: String::new(),
93            version: 0,
94            connection_state: String::new(),
95            pingtime: None,
96            pingwait: None,
97        }
98    }
99}