1mod types;
5pub use types::*;
6
7use std::num::NonZeroU64;
8use std::str::FromStr;
9use std::sync::OnceLock;
10use std::time::Instant;
11
12use crate::libp2p::chain_exchange::TipsetBundle;
13use crate::libp2p::{NetRPCMethods, NetworkMessage, PeerId};
14use crate::prelude::*;
15use crate::rpc::types::ApiTipsetKey;
16use crate::rpc::{ApiPaths, Ctx, Permission, RpcMethod, ServerError};
17use anyhow::Result;
18use cid::multibase;
19use enumflags2::BitFlags;
20
21pub enum NetAddrsListen {}
22impl RpcMethod<0> for NetAddrsListen {
23 const NAME: &'static str = "Filecoin.NetAddrsListen";
24 const PARAM_NAMES: [&'static str; 0] = [];
25 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
26 const PERMISSION: Permission = Permission::Read;
27 const DESCRIPTION: &'static str = "Returns a list of listening addresses and the peer ID.";
28
29 type Params = ();
30 type Ok = AddrInfo;
31
32 async fn handle(
33 ctx: Ctx,
34 (): Self::Params,
35 _: &http::Extensions,
36 ) -> Result<Self::Ok, ServerError> {
37 let (tx, rx) = flume::bounded(1);
38 let req = NetworkMessage::JSONRPCRequest {
39 method: NetRPCMethods::AddrsListen(tx),
40 };
41
42 ctx.network_send().send_async(req).await?;
43 let (id, addrs) = rx.recv_async().await?;
44
45 Ok(AddrInfo::new(id, addrs))
46 }
47}
48
49pub enum NetPeers {}
50impl RpcMethod<0> for NetPeers {
51 const NAME: &'static str = "Filecoin.NetPeers";
52 const PARAM_NAMES: [&'static str; 0] = [];
53 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
54 const PERMISSION: Permission = Permission::Read;
55 const DESCRIPTION: &'static str = "Returns a list of currently connected peers.";
56
57 type Params = ();
58 type Ok = Vec<AddrInfo>;
59
60 async fn handle(
61 ctx: Ctx,
62 (): Self::Params,
63 _: &http::Extensions,
64 ) -> Result<Self::Ok, ServerError> {
65 let (tx, rx) = flume::bounded(1);
66 let req = NetworkMessage::JSONRPCRequest {
67 method: NetRPCMethods::Peers(tx),
68 };
69
70 ctx.network_send().send_async(req).await?;
71 let peer_addresses = rx.recv_async().await?;
72
73 let connections = peer_addresses
74 .into_iter()
75 .map(|(id, addrs)| AddrInfo::new(id, addrs))
76 .collect();
77
78 Ok(connections)
79 }
80}
81
82pub enum NetFindPeer {}
83impl RpcMethod<1> for NetFindPeer {
84 const NAME: &'static str = "Filecoin.NetFindPeer";
85 const PARAM_NAMES: [&'static str; 1] = ["peerId"];
86 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
87 const PERMISSION: Permission = Permission::Read;
88 const DESCRIPTION: &'static str =
89 "Returns the known addresses of the peer with the given peer ID.";
90
91 type Params = (String,);
92 type Ok = AddrInfo;
93
94 async fn handle(
95 ctx: Ctx,
96 (peer_id,): Self::Params,
97 _: &http::Extensions,
98 ) -> Result<Self::Ok, ServerError> {
99 let peer_id = PeerId::from_str(&peer_id)?;
100 let (tx, rx) = flume::bounded(1);
101 ctx.network_send()
102 .send_async(NetworkMessage::JSONRPCRequest {
103 method: NetRPCMethods::Peer(tx, peer_id),
104 })
105 .await?;
106 let addrs = rx
107 .recv_async()
108 .await?
109 .with_context(|| format!("peer {peer_id} not found"))?;
110 Ok(AddrInfo::new(peer_id, addrs))
111 }
112}
113
114pub enum NetListening {}
115impl RpcMethod<0> for NetListening {
116 const NAME: &'static str = "Filecoin.NetListening";
117 const PARAM_NAMES: [&'static str; 0] = [];
118 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all_with_v2();
119 const PERMISSION: Permission = Permission::Read;
120 const NAME_ALIAS: Option<&'static str> = Some("net_listening");
121 const DESCRIPTION: &'static str =
122 "Returns whether the node is listening for network connections (always `true`).";
123
124 type Params = ();
125 type Ok = bool;
126
127 async fn handle(
128 _: Ctx,
129 (): Self::Params,
130 _: &http::Extensions,
131 ) -> Result<Self::Ok, ServerError> {
132 Ok(true)
133 }
134}
135
136pub enum NetInfo {}
137impl RpcMethod<0> for NetInfo {
138 const NAME: &'static str = "Forest.NetInfo";
139 const PARAM_NAMES: [&'static str; 0] = [];
140 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
141 const PERMISSION: Permission = Permission::Read;
142 const DESCRIPTION: &'static str =
143 "Returns peer and connection statistics for the node's libp2p network.";
144
145 type Params = ();
146 type Ok = NetInfoResult;
147
148 async fn handle(
149 ctx: Ctx,
150 (): Self::Params,
151 _: &http::Extensions,
152 ) -> Result<Self::Ok, ServerError> {
153 let (tx, rx) = flume::bounded(1);
154 let req = NetworkMessage::JSONRPCRequest {
155 method: NetRPCMethods::Info(tx),
156 };
157
158 ctx.network_send().send_async(req).await?;
159 Ok(rx.recv_async().await?)
160 }
161}
162
163pub enum NetBandwidthStats {}
164impl RpcMethod<0> for NetBandwidthStats {
165 const NAME: &'static str = "Filecoin.NetBandwidthStats";
166 const PARAM_NAMES: [&'static str; 0] = [];
167 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
168 const PERMISSION: Permission = Permission::Read;
169 const DESCRIPTION: &'static str = "Returns statistics about the node's total bandwidth. Currently a stub that always returns zeros.";
170
171 type Params = ();
172 type Ok = BandwidthStats;
173
174 async fn handle(
177 _: Ctx,
178 (): Self::Params,
179 _: &http::Extensions,
180 ) -> Result<Self::Ok, ServerError> {
181 Ok(BandwidthStats::default())
182 }
183}
184
185pub enum NetConnect {}
186impl RpcMethod<1> for NetConnect {
187 const NAME: &'static str = "Filecoin.NetConnect";
188 const PARAM_NAMES: [&'static str; 1] = ["peerAddressInfo"];
189 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
190 const PERMISSION: Permission = Permission::Write;
191 const DESCRIPTION: &'static str = "Connects to a specified peer.";
192
193 type Params = (AddrInfo,);
194 type Ok = ();
195
196 async fn handle(
197 ctx: Ctx,
198 (AddrInfo { id, addrs },): Self::Params,
199 _: &http::Extensions,
200 ) -> Result<Self::Ok, ServerError> {
201 let (_, id) = multibase::decode(format!("{}{}", "z", id))?;
202 let peer_id = PeerId::from_bytes(&id)?;
203
204 let (tx, rx) = flume::bounded(1);
205 let req = NetworkMessage::JSONRPCRequest {
206 method: NetRPCMethods::Connect(tx, peer_id, addrs),
207 };
208
209 ctx.network_send().send_async(req).await?;
210 let success = rx.recv_async().await?;
211
212 if success {
213 Ok(())
214 } else {
215 Err(anyhow::anyhow!("Peer could not be dialed from any address provided").into())
216 }
217 }
218}
219
220pub enum NetDisconnect {}
221impl RpcMethod<1> for NetDisconnect {
222 const NAME: &'static str = "Filecoin.NetDisconnect";
223 const PARAM_NAMES: [&'static str; 1] = ["peerId"];
224 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
225 const PERMISSION: Permission = Permission::Write;
226 const DESCRIPTION: &'static str = "Disconnects from the specified peer.";
227
228 type Params = (String,);
229 type Ok = ();
230
231 async fn handle(
232 ctx: Ctx,
233 (peer_id,): Self::Params,
234 _: &http::Extensions,
235 ) -> Result<Self::Ok, ServerError> {
236 let peer_id = PeerId::from_str(&peer_id)?;
237
238 let (tx, rx) = flume::bounded(1);
239 let req = NetworkMessage::JSONRPCRequest {
240 method: NetRPCMethods::Disconnect(tx, peer_id),
241 };
242
243 ctx.network_send().send_async(req).await?;
244 rx.recv_async().await?;
245
246 Ok(())
247 }
248}
249
250pub enum NetAgentVersion {}
251impl RpcMethod<1> for NetAgentVersion {
252 const NAME: &'static str = "Filecoin.NetAgentVersion";
253 const PARAM_NAMES: [&'static str; 1] = ["peerId"];
254 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
255 const PERMISSION: Permission = Permission::Read;
256 const DESCRIPTION: &'static str = "Returns the agent version string.";
257
258 type Params = (String,);
259 type Ok = String;
260
261 async fn handle(
262 ctx: Ctx,
263 (peer_id,): Self::Params,
264 _: &http::Extensions,
265 ) -> Result<Self::Ok, ServerError> {
266 let peer_id = PeerId::from_str(&peer_id)?;
267 let (tx, rx) = flume::bounded(1);
268 ctx.network_send()
269 .send_async(NetworkMessage::JSONRPCRequest {
270 method: NetRPCMethods::AgentVersion(tx, peer_id),
271 })
272 .await?;
273 Ok(rx.recv_async().await?.context("item not found")?)
274 }
275}
276
277pub enum NetAutoNatStatus {}
278impl RpcMethod<0> for NetAutoNatStatus {
279 const NAME: &'static str = "Filecoin.NetAutoNatStatus";
280 const PARAM_NAMES: [&'static str; 0] = [];
281 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
282 const PERMISSION: Permission = Permission::Read;
283 const DESCRIPTION: &'static str =
284 "Returns the node's AutoNAT reachability status and observed public addresses.";
285
286 type Params = ();
287 type Ok = NatStatusResult;
288
289 async fn handle(
290 ctx: Ctx,
291 (): Self::Params,
292 _: &http::Extensions,
293 ) -> Result<Self::Ok, ServerError> {
294 let (tx, rx) = flume::bounded(1);
295 let req = NetworkMessage::JSONRPCRequest {
296 method: NetRPCMethods::AutoNATStatus(tx),
297 };
298 ctx.network_send().send_async(req).await?;
299 let nat_status = rx.recv_async().await?;
300 Ok(nat_status.into())
301 }
302}
303
304pub enum NetVersion {}
305impl RpcMethod<0> for NetVersion {
306 const NAME: &'static str = "Filecoin.NetVersion";
307 const PARAM_NAMES: [&'static str; 0] = [];
308 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all_with_v2();
309 const PERMISSION: Permission = Permission::Read;
310 const NAME_ALIAS: Option<&'static str> = Some("net_version");
311 const DESCRIPTION: &'static str =
312 "Returns the current network ID (the EIP-155 chain ID) as a decimal string.";
313
314 type Params = ();
315 type Ok = Arc<str>;
316
317 async fn handle(
318 ctx: Ctx,
319 (): Self::Params,
320 _: &http::Extensions,
321 ) -> Result<Self::Ok, ServerError> {
322 static CACHED: OnceLock<Arc<str>> = OnceLock::new();
324 Ok(CACHED
325 .get_or_init(|| Arc::<str>::from(ctx.chain_config().eth_chain_id.to_string()))
326 .clone())
327 }
328}
329
330pub enum NetProtectAdd {}
331impl RpcMethod<1> for NetProtectAdd {
332 const NAME: &'static str = "Filecoin.NetProtectAdd";
333 const PARAM_NAMES: [&'static str; 1] = ["peerIdList"];
334 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
335 const PERMISSION: Permission = Permission::Admin;
336 const DESCRIPTION: &'static str = "Protects a peer from having its connection(s) pruned in the event the libp2p host reaches its maximum number of peers.";
337
338 type Params = (Vec<String>,);
339 type Ok = ();
340
341 async fn handle(
346 ctx: Ctx,
347 (peer_ids,): Self::Params,
348 _: &http::Extensions,
349 ) -> Result<Self::Ok, ServerError> {
350 let peer_ids = peer_ids
351 .iter()
352 .map(String::as_str)
353 .map(PeerId::from_str)
354 .try_collect()?;
355 let (tx, rx) = flume::bounded(1);
356 ctx.network_send()
357 .send_async(NetworkMessage::JSONRPCRequest {
358 method: NetRPCMethods::ProtectPeer(tx, peer_ids),
359 })
360 .await?;
361 rx.recv_async().await?;
362 Ok(())
363 }
364}
365
366pub enum NetProtectList {}
367impl RpcMethod<0> for NetProtectList {
368 const NAME: &'static str = "Filecoin.NetProtectList";
369 const PARAM_NAMES: [&'static str; 0] = [];
370 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
371 const PERMISSION: Permission = Permission::Read;
372 const DESCRIPTION: &'static str = "Returns the current list of protected peers.";
373
374 type Params = ();
375 type Ok = Vec<String>;
376 async fn handle(
377 ctx: Ctx,
378 (): Self::Params,
379 _: &http::Extensions,
380 ) -> Result<Self::Ok, ServerError> {
381 let (tx, rx) = flume::bounded(1);
382 ctx.network_send()
383 .send_async(NetworkMessage::JSONRPCRequest {
384 method: NetRPCMethods::ListProtectedPeers(tx),
385 })
386 .await?;
387 let peers = rx.recv_async().await?;
388 Ok(peers.into_iter().map(|p| p.to_string()).collect())
389 }
390}
391
392pub enum NetProtectRemove {}
393impl RpcMethod<1> for NetProtectRemove {
394 const NAME: &'static str = "Filecoin.NetProtectRemove";
395 const PARAM_NAMES: [&'static str; 1] = ["peerIdList"];
396 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
397 const PERMISSION: Permission = Permission::Admin;
398 const DESCRIPTION: &'static str = "Remove a peer from the protected list.";
399
400 type Params = (Vec<String>,);
401 type Ok = ();
402
403 async fn handle(
405 ctx: Ctx,
406 (peer_ids,): Self::Params,
407 _: &http::Extensions,
408 ) -> Result<Self::Ok, ServerError> {
409 let peer_ids = peer_ids
410 .iter()
411 .map(String::as_str)
412 .map(PeerId::from_str)
413 .try_collect()?;
414 let (tx, rx) = flume::bounded(1);
415 ctx.network_send()
416 .send_async(NetworkMessage::JSONRPCRequest {
417 method: NetRPCMethods::UnprotectPeer(tx, peer_ids),
418 })
419 .await?;
420 rx.recv_async().await?;
421 Ok(())
422 }
423}
424
425pub enum NetChainExchange {}
426impl RpcMethod<3> for NetChainExchange {
427 const NAME: &'static str = "Forest.NetChainExchange";
428 const PARAM_NAMES: [&'static str; 3] = ["startTipsetKey", "len", "options"];
429 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all_with_v2();
430 const PERMISSION: Permission = Permission::Admin;
431 const DESCRIPTION: &'static str = "Internal API for debugging chain exchange.";
432
433 type Params = (ApiTipsetKey, u64, u64);
434 type Ok = String;
435
436 async fn handle(
437 ctx: Ctx,
438 (tsk, request_len, options): Self::Params,
439 _: &http::Extensions,
440 ) -> Result<Self::Ok, ServerError> {
441 let request_len =
442 NonZeroU64::new(request_len).context("request length must be greater than 0")?;
443 let tsk = tsk
444 .0
445 .unwrap_or_else(|| ctx.chain_store().heaviest_tipset().key().clone());
446 let timer = Instant::now();
447 let result: Vec<TipsetBundle> = ctx
448 .sync_network_context
449 .handle_chain_exchange_request(None, &tsk, request_len, options, |_| true)
450 .await?;
451 Ok(format!(
452 "fetched {} tipsets, took {}",
453 result.len(),
454 humantime::format_duration(timer.elapsed())
455 ))
456 }
457}