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 NetConnect {}
164impl RpcMethod<1> for NetConnect {
165 const NAME: &'static str = "Filecoin.NetConnect";
166 const PARAM_NAMES: [&'static str; 1] = ["peerAddressInfo"];
167 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
168 const PERMISSION: Permission = Permission::Write;
169 const DESCRIPTION: &'static str = "Connects to a specified peer.";
170
171 type Params = (AddrInfo,);
172 type Ok = ();
173
174 async fn handle(
175 ctx: Ctx,
176 (AddrInfo { id, addrs },): Self::Params,
177 _: &http::Extensions,
178 ) -> Result<Self::Ok, ServerError> {
179 let (_, id) = multibase::decode(format!("{}{}", "z", id))?;
180 let peer_id = PeerId::from_bytes(&id)?;
181
182 let (tx, rx) = flume::bounded(1);
183 let req = NetworkMessage::JSONRPCRequest {
184 method: NetRPCMethods::Connect(tx, peer_id, addrs),
185 };
186
187 ctx.network_send().send_async(req).await?;
188 let success = rx.recv_async().await?;
189
190 if success {
191 Ok(())
192 } else {
193 Err(anyhow::anyhow!("Peer could not be dialed from any address provided").into())
194 }
195 }
196}
197
198pub enum NetDisconnect {}
199impl RpcMethod<1> for NetDisconnect {
200 const NAME: &'static str = "Filecoin.NetDisconnect";
201 const PARAM_NAMES: [&'static str; 1] = ["peerId"];
202 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
203 const PERMISSION: Permission = Permission::Write;
204 const DESCRIPTION: &'static str = "Disconnects from the specified peer.";
205
206 type Params = (String,);
207 type Ok = ();
208
209 async fn handle(
210 ctx: Ctx,
211 (peer_id,): Self::Params,
212 _: &http::Extensions,
213 ) -> Result<Self::Ok, ServerError> {
214 let peer_id = PeerId::from_str(&peer_id)?;
215
216 let (tx, rx) = flume::bounded(1);
217 let req = NetworkMessage::JSONRPCRequest {
218 method: NetRPCMethods::Disconnect(tx, peer_id),
219 };
220
221 ctx.network_send().send_async(req).await?;
222 rx.recv_async().await?;
223
224 Ok(())
225 }
226}
227
228pub enum NetAgentVersion {}
229impl RpcMethod<1> for NetAgentVersion {
230 const NAME: &'static str = "Filecoin.NetAgentVersion";
231 const PARAM_NAMES: [&'static str; 1] = ["peerId"];
232 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
233 const PERMISSION: Permission = Permission::Read;
234 const DESCRIPTION: &'static str = "Returns the agent version string.";
235
236 type Params = (String,);
237 type Ok = String;
238
239 async fn handle(
240 ctx: Ctx,
241 (peer_id,): Self::Params,
242 _: &http::Extensions,
243 ) -> Result<Self::Ok, ServerError> {
244 let peer_id = PeerId::from_str(&peer_id)?;
245 let (tx, rx) = flume::bounded(1);
246 ctx.network_send()
247 .send_async(NetworkMessage::JSONRPCRequest {
248 method: NetRPCMethods::AgentVersion(tx, peer_id),
249 })
250 .await?;
251 Ok(rx.recv_async().await?.context("item not found")?)
252 }
253}
254
255pub enum NetAutoNatStatus {}
256impl RpcMethod<0> for NetAutoNatStatus {
257 const NAME: &'static str = "Filecoin.NetAutoNatStatus";
258 const PARAM_NAMES: [&'static str; 0] = [];
259 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
260 const PERMISSION: Permission = Permission::Read;
261 const DESCRIPTION: &'static str =
262 "Returns the node's AutoNAT reachability status and observed public addresses.";
263
264 type Params = ();
265 type Ok = NatStatusResult;
266
267 async fn handle(
268 ctx: Ctx,
269 (): Self::Params,
270 _: &http::Extensions,
271 ) -> Result<Self::Ok, ServerError> {
272 let (tx, rx) = flume::bounded(1);
273 let req = NetworkMessage::JSONRPCRequest {
274 method: NetRPCMethods::AutoNATStatus(tx),
275 };
276 ctx.network_send().send_async(req).await?;
277 let nat_status = rx.recv_async().await?;
278 Ok(nat_status.into())
279 }
280}
281
282pub enum NetVersion {}
283impl RpcMethod<0> for NetVersion {
284 const NAME: &'static str = "Filecoin.NetVersion";
285 const PARAM_NAMES: [&'static str; 0] = [];
286 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all_with_v2();
287 const PERMISSION: Permission = Permission::Read;
288 const NAME_ALIAS: Option<&'static str> = Some("net_version");
289 const DESCRIPTION: &'static str =
290 "Returns the current network ID (the EIP-155 chain ID) as a decimal string.";
291
292 type Params = ();
293 type Ok = Arc<str>;
294
295 async fn handle(
296 ctx: Ctx,
297 (): Self::Params,
298 _: &http::Extensions,
299 ) -> Result<Self::Ok, ServerError> {
300 static CACHED: OnceLock<Arc<str>> = OnceLock::new();
302 Ok(CACHED
303 .get_or_init(|| Arc::<str>::from(ctx.chain_config().eth_chain_id.to_string()))
304 .clone())
305 }
306}
307
308pub enum NetProtectAdd {}
309impl RpcMethod<1> for NetProtectAdd {
310 const NAME: &'static str = "Filecoin.NetProtectAdd";
311 const PARAM_NAMES: [&'static str; 1] = ["peerIdList"];
312 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
313 const PERMISSION: Permission = Permission::Admin;
314 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.";
315
316 type Params = (Vec<String>,);
317 type Ok = ();
318
319 async fn handle(
324 ctx: Ctx,
325 (peer_ids,): Self::Params,
326 _: &http::Extensions,
327 ) -> Result<Self::Ok, ServerError> {
328 let peer_ids = peer_ids
329 .iter()
330 .map(String::as_str)
331 .map(PeerId::from_str)
332 .try_collect()?;
333 let (tx, rx) = flume::bounded(1);
334 ctx.network_send()
335 .send_async(NetworkMessage::JSONRPCRequest {
336 method: NetRPCMethods::ProtectPeer(tx, peer_ids),
337 })
338 .await?;
339 rx.recv_async().await?;
340 Ok(())
341 }
342}
343
344pub enum NetProtectList {}
345impl RpcMethod<0> for NetProtectList {
346 const NAME: &'static str = "Filecoin.NetProtectList";
347 const PARAM_NAMES: [&'static str; 0] = [];
348 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
349 const PERMISSION: Permission = Permission::Read;
350 const DESCRIPTION: &'static str = "Returns the current list of protected peers.";
351
352 type Params = ();
353 type Ok = Vec<String>;
354 async fn handle(
355 ctx: Ctx,
356 (): Self::Params,
357 _: &http::Extensions,
358 ) -> Result<Self::Ok, ServerError> {
359 let (tx, rx) = flume::bounded(1);
360 ctx.network_send()
361 .send_async(NetworkMessage::JSONRPCRequest {
362 method: NetRPCMethods::ListProtectedPeers(tx),
363 })
364 .await?;
365 let peers = rx.recv_async().await?;
366 Ok(peers.into_iter().map(|p| p.to_string()).collect())
367 }
368}
369
370pub enum NetProtectRemove {}
371impl RpcMethod<1> for NetProtectRemove {
372 const NAME: &'static str = "Filecoin.NetProtectRemove";
373 const PARAM_NAMES: [&'static str; 1] = ["peerIdList"];
374 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
375 const PERMISSION: Permission = Permission::Admin;
376 const DESCRIPTION: &'static str = "Remove a peer from the protected list.";
377
378 type Params = (Vec<String>,);
379 type Ok = ();
380
381 async fn handle(
383 ctx: Ctx,
384 (peer_ids,): Self::Params,
385 _: &http::Extensions,
386 ) -> Result<Self::Ok, ServerError> {
387 let peer_ids = peer_ids
388 .iter()
389 .map(String::as_str)
390 .map(PeerId::from_str)
391 .try_collect()?;
392 let (tx, rx) = flume::bounded(1);
393 ctx.network_send()
394 .send_async(NetworkMessage::JSONRPCRequest {
395 method: NetRPCMethods::UnprotectPeer(tx, peer_ids),
396 })
397 .await?;
398 rx.recv_async().await?;
399 Ok(())
400 }
401}
402
403pub enum NetChainExchange {}
404impl RpcMethod<3> for NetChainExchange {
405 const NAME: &'static str = "Forest.NetChainExchange";
406 const PARAM_NAMES: [&'static str; 3] = ["startTipsetKey", "len", "options"];
407 const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all_with_v2();
408 const PERMISSION: Permission = Permission::Admin;
409 const DESCRIPTION: &'static str = "Internal API for debugging chain exchange.";
410
411 type Params = (ApiTipsetKey, u64, u64);
412 type Ok = String;
413
414 async fn handle(
415 ctx: Ctx,
416 (tsk, request_len, options): Self::Params,
417 _: &http::Extensions,
418 ) -> Result<Self::Ok, ServerError> {
419 let request_len =
420 NonZeroU64::new(request_len).context("request length must be greater than 0")?;
421 let tsk = tsk
422 .0
423 .unwrap_or_else(|| ctx.chain_store().heaviest_tipset().key().clone());
424 let timer = Instant::now();
425 let result: Vec<TipsetBundle> = ctx
426 .sync_network_context
427 .handle_chain_exchange_request(None, &tsk, request_len, options, |_| true)
428 .await?;
429 Ok(format!(
430 "fetched {} tipsets, took {}",
431 result.len(),
432 humantime::format_duration(timer.elapsed())
433 ))
434 }
435}