Skip to main content

bee/debug/
peers.rs

1//! Peer/connectivity endpoints: peers list, blocklist, ping, connect,
2//! topology, reserve state. Mirrors bee-go's
3//! `pkg/debug/connectivity.go` plus the relevant pieces of `node.go`.
4
5use reqwest::Method;
6use serde::Deserialize;
7
8use crate::client::request;
9use crate::swarm::Error;
10
11use super::DebugApi;
12
13/// One connected peer entry. Mirrors bee-go `Peer`.
14#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
15pub struct Peer {
16    /// Peer overlay address (hex).
17    pub address: String,
18    /// True for full nodes, false for light nodes.
19    #[serde(default, rename = "fullNode")]
20    pub full_node: bool,
21}
22
23/// Node addresses payload — `GET /addresses`.
24#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
25#[serde(rename_all = "camelCase")]
26pub struct Addresses {
27    /// Overlay (DHT) address.
28    pub overlay: String,
29    /// Underlay multiaddrs.
30    pub underlay: Vec<String>,
31    /// Ethereum address.
32    pub ethereum: String,
33    /// Node libp2p public key (hex).
34    pub public_key: String,
35    /// PSS public key (hex).
36    pub pss_public_key: String,
37}
38
39/// Topology snapshot — `GET /topology`.
40#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
41#[serde(rename_all = "camelCase")]
42pub struct Topology {
43    /// Base address (overlay).
44    pub base_addr: String,
45    /// Population (peers known across all bins).
46    pub population: i64,
47    /// Currently connected peers.
48    pub connected: i64,
49    /// Snapshot timestamp (RFC 3339).
50    pub timestamp: String,
51    /// Lower watermark for the nearest neighbour bin.
52    pub nn_low_watermark: i64,
53    /// Kademlia depth.
54    pub depth: u8,
55}
56
57/// Reserve state snapshot — `GET /reservestate`.
58#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
59#[serde(rename_all = "camelCase")]
60pub struct ReserveState {
61    /// Network radius.
62    pub radius: u8,
63    /// Storage radius.
64    pub storage_radius: u8,
65    /// Batch commitment.
66    pub commitment: i64,
67}
68
69impl DebugApi {
70    /// `GET /peers` — list every peer this node is currently connected to.
71    pub async fn peers(&self) -> Result<Vec<Peer>, Error> {
72        let builder = request(&self.inner, Method::GET, "peers")?;
73        #[derive(Deserialize)]
74        struct Resp {
75            peers: Vec<Peer>,
76        }
77        let r: Resp = self.inner.send_json(builder).await?;
78        Ok(r.peers)
79    }
80
81    /// `GET /blocklist` — peers currently blocklisted by this node.
82    pub async fn blocklist(&self) -> Result<Vec<Peer>, Error> {
83        let builder = request(&self.inner, Method::GET, "blocklist")?;
84        #[derive(Deserialize)]
85        struct Resp {
86            peers: Vec<Peer>,
87        }
88        let r: Resp = self.inner.send_json(builder).await?;
89        Ok(r.peers)
90    }
91
92    /// `DELETE /peers/{address}` — disconnect and forget a peer.
93    pub async fn remove_peer(&self, address: &str) -> Result<(), Error> {
94        let path = format!("peers/{address}");
95        let builder = request(&self.inner, Method::DELETE, &path)?;
96        self.inner.send(builder).await?;
97        Ok(())
98    }
99
100    /// `POST /pingpong/{address}` — round-trip ping a peer. Returns the
101    /// reported RTT string (e.g. `"2.5ms"`).
102    pub async fn ping_peer(&self, address: &str) -> Result<String, Error> {
103        let path = format!("pingpong/{address}");
104        let builder = request(&self.inner, Method::POST, &path)?;
105        #[derive(Deserialize)]
106        struct Resp {
107            rtt: String,
108        }
109        let r: Resp = self.inner.send_json(builder).await?;
110        Ok(r.rtt)
111    }
112
113    /// `POST /connect/{multiaddr}` — manually dial a peer at the given
114    /// multiaddress (e.g. `"/dns/bee.example.com/tcp/1634/p2p/16Uiu…"`).
115    /// Returns the resulting overlay address. A leading `/` in
116    /// `multiaddr` is stripped.
117    pub async fn connect_peer(&self, multiaddr: &str) -> Result<String, Error> {
118        let trimmed = multiaddr.trim_start_matches('/');
119        let path = format!("connect/{trimmed}");
120        let builder = request(&self.inner, Method::POST, &path)?;
121        #[derive(Deserialize)]
122        struct Resp {
123            address: String,
124        }
125        let r: Resp = self.inner.send_json(builder).await?;
126        Ok(r.address)
127    }
128
129    /// `GET /addresses` — node overlay / underlay / ethereum / pubkeys.
130    pub async fn addresses(&self) -> Result<Addresses, Error> {
131        let builder = request(&self.inner, Method::GET, "addresses")?;
132        self.inner.send_json(builder).await
133    }
134
135    /// `GET /topology` — Kademlia topology snapshot.
136    pub async fn topology(&self) -> Result<Topology, Error> {
137        let builder = request(&self.inner, Method::GET, "topology")?;
138        self.inner.send_json(builder).await
139    }
140
141    /// `GET /reservestate` — current reserve radius/commitment.
142    pub async fn reserve_state(&self) -> Result<ReserveState, Error> {
143        let builder = request(&self.inner, Method::GET, "reservestate")?;
144        self.inner.send_json(builder).await
145    }
146
147    /// `GET /welcome-message` — P2P welcome banner.
148    pub async fn welcome_message(&self) -> Result<String, Error> {
149        let builder = request(&self.inner, Method::GET, "welcome-message")?;
150        #[derive(Deserialize)]
151        struct Resp {
152            #[serde(rename = "welcomeMessage")]
153            welcome_message: String,
154        }
155        let r: Resp = self.inner.send_json(builder).await?;
156        Ok(r.welcome_message)
157    }
158
159    /// `POST /welcome-message` — update the P2P welcome banner.
160    pub async fn set_welcome_message(&self, message: &str) -> Result<(), Error> {
161        #[derive(serde::Serialize)]
162        struct Body<'a> {
163            #[serde(rename = "welcomeMessage")]
164            welcome_message: &'a str,
165        }
166        let body = serde_json::to_vec(&Body {
167            welcome_message: message,
168        })?;
169        let builder = request(&self.inner, Method::POST, "welcome-message")?
170            .header("Content-Type", "application/json")
171            .body(bytes::Bytes::from(body));
172        self.inner.send(builder).await?;
173        Ok(())
174    }
175}