Skip to main content

forest/rpc/methods/
net.rs

1// Copyright 2019-2026 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4mod types;
5use itertools::Itertools;
6pub use types::*;
7
8use std::any::Any;
9use std::str::FromStr;
10
11use crate::libp2p::{NetRPCMethods, NetworkMessage, PeerId};
12use crate::rpc::{ApiPaths, Ctx, Permission, RpcMethod, ServerError};
13use anyhow::{Context as _, Result};
14use cid::multibase;
15use enumflags2::BitFlags;
16use fvm_ipld_blockstore::Blockstore;
17
18pub enum NetAddrsListen {}
19impl RpcMethod<0> for NetAddrsListen {
20    const NAME: &'static str = "Filecoin.NetAddrsListen";
21    const PARAM_NAMES: [&'static str; 0] = [];
22    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
23    const PERMISSION: Permission = Permission::Read;
24    const DESCRIPTION: Option<&'static str> =
25        Some("Returns a list of listening addresses and the peer ID.");
26
27    type Params = ();
28    type Ok = AddrInfo;
29
30    async fn handle(
31        ctx: Ctx<impl Blockstore>,
32        (): Self::Params,
33        _: &http::Extensions,
34    ) -> Result<Self::Ok, ServerError> {
35        let (tx, rx) = flume::bounded(1);
36        let req = NetworkMessage::JSONRPCRequest {
37            method: NetRPCMethods::AddrsListen(tx),
38        };
39
40        ctx.network_send().send_async(req).await?;
41        let (id, addrs) = rx.recv_async().await?;
42
43        Ok(AddrInfo::new(id, addrs))
44    }
45}
46
47pub enum NetPeers {}
48impl RpcMethod<0> for NetPeers {
49    const NAME: &'static str = "Filecoin.NetPeers";
50    const PARAM_NAMES: [&'static str; 0] = [];
51    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
52    const PERMISSION: Permission = Permission::Read;
53    const DESCRIPTION: Option<&'static str> = Some("Returns a list of currently connected peers.");
54
55    type Params = ();
56    type Ok = Vec<AddrInfo>;
57
58    async fn handle(
59        ctx: Ctx<impl Blockstore>,
60        (): Self::Params,
61        _: &http::Extensions,
62    ) -> Result<Self::Ok, ServerError> {
63        let (tx, rx) = flume::bounded(1);
64        let req = NetworkMessage::JSONRPCRequest {
65            method: NetRPCMethods::Peers(tx),
66        };
67
68        ctx.network_send().send_async(req).await?;
69        let peer_addresses = rx.recv_async().await?;
70
71        let connections = peer_addresses
72            .into_iter()
73            .map(|(id, addrs)| AddrInfo::new(id, addrs))
74            .collect();
75
76        Ok(connections)
77    }
78}
79
80pub enum NetFindPeer {}
81impl RpcMethod<1> for NetFindPeer {
82    const NAME: &'static str = "Filecoin.NetFindPeer";
83    const PARAM_NAMES: [&'static str; 1] = ["peer_id"];
84    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
85    const PERMISSION: Permission = Permission::Read;
86
87    type Params = (String,);
88    type Ok = AddrInfo;
89
90    async fn handle(
91        ctx: Ctx<impl Blockstore>,
92        (peer_id,): Self::Params,
93        _: &http::Extensions,
94    ) -> Result<Self::Ok, ServerError> {
95        let peer_id = PeerId::from_str(&peer_id)?;
96        let (tx, rx) = flume::bounded(1);
97        ctx.network_send()
98            .send_async(NetworkMessage::JSONRPCRequest {
99                method: NetRPCMethods::Peer(tx, peer_id),
100            })
101            .await?;
102        let addrs = rx
103            .recv_async()
104            .await?
105            .with_context(|| format!("peer {peer_id} not found"))?;
106        Ok(AddrInfo::new(peer_id, addrs))
107    }
108}
109
110pub enum NetListening {}
111impl RpcMethod<0> for NetListening {
112    const NAME: &'static str = "Filecoin.NetListening";
113    const PARAM_NAMES: [&'static str; 0] = [];
114    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all_with_v2();
115    const PERMISSION: Permission = Permission::Read;
116    const NAME_ALIAS: Option<&'static str> = Some("net_listening");
117
118    type Params = ();
119    type Ok = bool;
120
121    async fn handle(
122        _: Ctx<impl Any>,
123        (): Self::Params,
124        _: &http::Extensions,
125    ) -> Result<Self::Ok, ServerError> {
126        Ok(true)
127    }
128}
129
130pub enum NetInfo {}
131impl RpcMethod<0> for NetInfo {
132    const NAME: &'static str = "Forest.NetInfo";
133    const PARAM_NAMES: [&'static str; 0] = [];
134    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
135    const PERMISSION: Permission = Permission::Read;
136
137    type Params = ();
138    type Ok = NetInfoResult;
139
140    async fn handle(
141        ctx: Ctx<impl Blockstore>,
142        (): Self::Params,
143        _: &http::Extensions,
144    ) -> Result<Self::Ok, ServerError> {
145        let (tx, rx) = flume::bounded(1);
146        let req = NetworkMessage::JSONRPCRequest {
147            method: NetRPCMethods::Info(tx),
148        };
149
150        ctx.network_send().send_async(req).await?;
151        Ok(rx.recv_async().await?)
152    }
153}
154
155pub enum NetConnect {}
156impl RpcMethod<1> for NetConnect {
157    const NAME: &'static str = "Filecoin.NetConnect";
158    const PARAM_NAMES: [&'static str; 1] = ["peerAddressInfo"];
159    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
160    const PERMISSION: Permission = Permission::Write;
161    const DESCRIPTION: Option<&'static str> = Some("Connects to a specified peer.");
162
163    type Params = (AddrInfo,);
164    type Ok = ();
165
166    async fn handle(
167        ctx: Ctx<impl Blockstore>,
168        (AddrInfo { id, addrs },): Self::Params,
169        _: &http::Extensions,
170    ) -> Result<Self::Ok, ServerError> {
171        let (_, id) = multibase::decode(format!("{}{}", "z", id))?;
172        let peer_id = PeerId::from_bytes(&id)?;
173
174        let (tx, rx) = flume::bounded(1);
175        let req = NetworkMessage::JSONRPCRequest {
176            method: NetRPCMethods::Connect(tx, peer_id, addrs),
177        };
178
179        ctx.network_send().send_async(req).await?;
180        let success = rx.recv_async().await?;
181
182        if success {
183            Ok(())
184        } else {
185            Err(anyhow::anyhow!("Peer could not be dialed from any address provided").into())
186        }
187    }
188}
189
190pub enum NetDisconnect {}
191impl RpcMethod<1> for NetDisconnect {
192    const NAME: &'static str = "Filecoin.NetDisconnect";
193    const PARAM_NAMES: [&'static str; 1] = ["peerId"];
194    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
195    const PERMISSION: Permission = Permission::Write;
196    const DESCRIPTION: Option<&'static str> = Some("Disconnects from the specified peer.");
197
198    type Params = (String,);
199    type Ok = ();
200
201    async fn handle(
202        ctx: Ctx<impl Blockstore>,
203        (peer_id,): Self::Params,
204        _: &http::Extensions,
205    ) -> Result<Self::Ok, ServerError> {
206        let peer_id = PeerId::from_str(&peer_id)?;
207
208        let (tx, rx) = flume::bounded(1);
209        let req = NetworkMessage::JSONRPCRequest {
210            method: NetRPCMethods::Disconnect(tx, peer_id),
211        };
212
213        ctx.network_send().send_async(req).await?;
214        rx.recv_async().await?;
215
216        Ok(())
217    }
218}
219
220pub enum NetAgentVersion {}
221impl RpcMethod<1> for NetAgentVersion {
222    const NAME: &'static str = "Filecoin.NetAgentVersion";
223    const PARAM_NAMES: [&'static str; 1] = ["peerId"];
224    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
225    const PERMISSION: Permission = Permission::Read;
226    const DESCRIPTION: Option<&'static str> = Some("Returns the agent version string.");
227
228    type Params = (String,);
229    type Ok = String;
230
231    async fn handle(
232        ctx: Ctx<impl Blockstore>,
233        (peer_id,): Self::Params,
234        _: &http::Extensions,
235    ) -> Result<Self::Ok, ServerError> {
236        let peer_id = PeerId::from_str(&peer_id)?;
237        let (tx, rx) = flume::bounded(1);
238        ctx.network_send()
239            .send_async(NetworkMessage::JSONRPCRequest {
240                method: NetRPCMethods::AgentVersion(tx, peer_id),
241            })
242            .await?;
243        Ok(rx.recv_async().await?.context("item not found")?)
244    }
245}
246
247pub enum NetAutoNatStatus {}
248impl RpcMethod<0> for NetAutoNatStatus {
249    const NAME: &'static str = "Filecoin.NetAutoNatStatus";
250    const PARAM_NAMES: [&'static str; 0] = [];
251    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
252    const PERMISSION: Permission = Permission::Read;
253
254    type Params = ();
255    type Ok = NatStatusResult;
256
257    async fn handle(
258        ctx: Ctx<impl Blockstore>,
259        (): Self::Params,
260        _: &http::Extensions,
261    ) -> Result<Self::Ok, ServerError> {
262        let (tx, rx) = flume::bounded(1);
263        let req = NetworkMessage::JSONRPCRequest {
264            method: NetRPCMethods::AutoNATStatus(tx),
265        };
266        ctx.network_send().send_async(req).await?;
267        let nat_status = rx.recv_async().await?;
268        Ok(nat_status.into())
269    }
270}
271
272pub enum NetVersion {}
273impl RpcMethod<0> for NetVersion {
274    const NAME: &'static str = "Filecoin.NetVersion";
275    const PARAM_NAMES: [&'static str; 0] = [];
276    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all_with_v2();
277    const PERMISSION: Permission = Permission::Read;
278    const NAME_ALIAS: Option<&'static str> = Some("net_version");
279
280    type Params = ();
281    type Ok = String;
282
283    async fn handle(
284        ctx: Ctx<impl Blockstore>,
285        (): Self::Params,
286        _: &http::Extensions,
287    ) -> Result<Self::Ok, ServerError> {
288        Ok(ctx.chain_config().eth_chain_id.to_string())
289    }
290}
291
292pub enum NetProtectAdd {}
293impl RpcMethod<1> for NetProtectAdd {
294    const NAME: &'static str = "Filecoin.NetProtectAdd";
295    const PARAM_NAMES: [&'static str; 1] = ["peerIdList"];
296    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
297    const PERMISSION: Permission = Permission::Admin;
298    const DESCRIPTION: Option<&'static str> = Some(
299        "Protects a peer from having its connection(s) pruned in the event the libp2p host reaches its maximum number of peers.",
300    );
301
302    type Params = (Vec<String>,);
303    type Ok = ();
304
305    // This whitelists a peer in forest peer manager but has no impact on libp2p swarm
306    // due to the fact that `rust-libp2p` implementation is very different to that
307    // in go. However it would be nice to investigate connection limiting options in Rust.
308    // See: <https://github.com/ChainSafe/forest/issues/4355>.
309    async fn handle(
310        ctx: Ctx<impl Blockstore>,
311        (peer_ids,): Self::Params,
312        _: &http::Extensions,
313    ) -> Result<Self::Ok, ServerError> {
314        let peer_ids = peer_ids
315            .iter()
316            .map(String::as_str)
317            .map(PeerId::from_str)
318            .try_collect()?;
319        let (tx, rx) = flume::bounded(1);
320        ctx.network_send()
321            .send_async(NetworkMessage::JSONRPCRequest {
322                method: NetRPCMethods::ProtectPeer(tx, peer_ids),
323            })
324            .await?;
325        rx.recv_async().await?;
326        Ok(())
327    }
328}
329
330pub enum NetProtectList {}
331impl RpcMethod<0> for NetProtectList {
332    const NAME: &'static str = "Filecoin.NetProtectList";
333    const PARAM_NAMES: [&'static str; 0] = [];
334    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
335    const PERMISSION: Permission = Permission::Read;
336    const DESCRIPTION: Option<&'static str> = Some("Returns the current list of protected peers.");
337
338    type Params = ();
339    type Ok = Vec<String>;
340    async fn handle(
341        ctx: Ctx<impl Blockstore>,
342        (): Self::Params,
343        _: &http::Extensions,
344    ) -> Result<Self::Ok, ServerError> {
345        let (tx, rx) = flume::bounded(1);
346        ctx.network_send()
347            .send_async(NetworkMessage::JSONRPCRequest {
348                method: NetRPCMethods::ListProtectedPeers(tx),
349            })
350            .await?;
351        let peers = rx.recv_async().await?;
352        Ok(peers.into_iter().map(|p| p.to_string()).collect())
353    }
354}
355
356pub enum NetProtectRemove {}
357impl RpcMethod<1> for NetProtectRemove {
358    const NAME: &'static str = "Filecoin.NetProtectRemove";
359    const PARAM_NAMES: [&'static str; 1] = ["peerIdList"];
360    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
361    const PERMISSION: Permission = Permission::Admin;
362    const DESCRIPTION: Option<&'static str> = Some("Remove a peer from the protected list.");
363
364    type Params = (Vec<String>,);
365    type Ok = ();
366
367    // Similar to NetProtectAdd
368    async fn handle(
369        ctx: Ctx<impl Blockstore>,
370        (peer_ids,): Self::Params,
371        _: &http::Extensions,
372    ) -> Result<Self::Ok, ServerError> {
373        let peer_ids = peer_ids
374            .iter()
375            .map(String::as_str)
376            .map(PeerId::from_str)
377            .try_collect()?;
378        let (tx, rx) = flume::bounded(1);
379        ctx.network_send()
380            .send_async(NetworkMessage::JSONRPCRequest {
381                method: NetRPCMethods::UnprotectPeer(tx, peer_ids),
382            })
383            .await?;
384        rx.recv_async().await?;
385        Ok(())
386    }
387}