forest/cli/subcommands/
net_cmd.rs1use crate::libp2p::{Multiaddr, Protocol};
5use crate::rpc::{self, net::AddrInfo, prelude::*};
6use ahash::{HashMap, HashSet};
7use cid::multibase;
8use clap::Subcommand;
9use itertools::Itertools;
10
11use crate::cli::subcommands::cli_error_and_die;
12
13#[derive(Debug, Subcommand)]
14pub enum NetCommands {
15 Listen,
17 Info,
19 Peers {
21 #[arg(short, long)]
23 agent: bool,
24 },
25 Connect {
27 address: String,
29 },
30 Disconnect {
32 id: String,
34 },
35 Reachability,
37}
38
39impl NetCommands {
40 pub async fn run(self, client: rpc::Client) -> anyhow::Result<()> {
41 match self {
42 Self::Listen => {
43 let info = NetAddrsListen::call(&client, ()).await?;
44 let addresses: Vec<String> = info
45 .addrs
46 .iter()
47 .map(|addr| format!("{}/p2p/{}", addr, info.id))
48 .collect();
49 println!("{}", addresses.join("\n"));
50 Ok(())
51 }
52 Self::Info => {
53 let info = NetInfo::call(&client, ()).await?;
54 println!("forest libp2p swarm info:");
55 println!("num peers: {}", info.num_peers);
56 println!("num connections: {}", info.num_connections);
57 println!("num pending: {}", info.num_pending);
58 println!("num pending incoming: {}", info.num_pending_incoming);
59 println!("num pending outgoing: {}", info.num_pending_outgoing);
60 println!("num established: {}", info.num_established);
61 Ok(())
62 }
63 Self::Peers { agent } => {
64 let addrs = NetPeers::call(&client, ()).await?;
65 let peer_to_agents: HashMap<String, String> = if agent {
66 let agents = futures::future::join_all(
67 addrs
68 .iter()
69 .map(|info| NetAgentVersion::call(&client, (info.id.clone(),))),
70 )
71 .await
72 .into_iter()
73 .map(|res| res.unwrap_or_else(|_| "<agent unknown>".to_owned()));
74
75 HashMap::from_iter(
76 addrs
77 .iter()
78 .map(|info| info.id.to_owned())
79 .zip(agents.into_iter()),
80 )
81 } else {
82 HashMap::default()
83 };
84
85 let output: Vec<String> = addrs
86 .into_iter()
87 .filter_map(|info| {
88 let addresses: Vec<String> = info
89 .addrs
90 .into_iter()
91 .filter(|addr| match addr.iter().next().unwrap() {
92 Protocol::Ip4(ip_addr) => !ip_addr.is_loopback(),
93 Protocol::Ip6(ip_addr) => !ip_addr.is_loopback(),
94 _ => true,
95 })
96 .map(|addr| addr.to_string())
97 .unique()
98 .collect();
99 if addresses.is_empty() {
100 return None;
101 }
102
103 let result = format!("{}, [{}]", info.id, addresses.join(", "));
104
105 if agent {
106 Some(
107 [
108 result,
109 peer_to_agents
110 .get(&info.id)
111 .cloned()
112 .unwrap_or_else(|| "<agent unknown>".to_owned()),
113 ]
114 .join(", "),
115 )
116 } else {
117 Some(result)
118 }
119 })
120 .collect();
121 println!("{}", output.join("\n"));
122 Ok(())
123 }
124 Self::Connect { address } => {
125 let addr: Multiaddr = address
126 .parse()
127 .map_err(|e| {
128 cli_error_and_die(format!("Error parsing multiaddr. Error was: {e}"), 1);
129 })
130 .expect("Parse provided multiaddr from string");
131
132 let mut id = "".to_owned();
133
134 for protocol in addr.iter() {
135 if let Protocol::P2p(p2p) = protocol {
136 id = multibase::encode(multibase::Base::Base58Btc, p2p.to_bytes());
137 id = id.split_off(1);
138 }
139 }
140
141 if id.is_empty() {
142 cli_error_and_die("Needs a /p2p/ protocol present in multiaddr", 1)
143 }
144
145 let addrs = HashSet::from_iter([addr]);
146 let addr_info = AddrInfo {
147 id: id.clone(),
148 addrs,
149 };
150
151 NetConnect::call(&client, (addr_info,)).await?;
152 println!("connect {id}: success");
153 Ok(())
154 }
155 Self::Disconnect { id } => {
156 NetDisconnect::call(&client, (id.clone(),)).await?;
157 println!("disconnect {id}: success");
158 Ok(())
159 }
160 Self::Reachability => {
161 let nat_status = NetAutoNatStatus::call(&client, ()).await?;
162 println!("AutoNAT status: {}", nat_status.reachability_as_str());
163 if let Some(public_addrs) = nat_status.public_addrs
164 && !public_addrs.is_empty()
165 {
166 println!("Public address: [{}]", public_addrs.join(" "));
169 }
170 Ok(())
171 }
172 }
173 }
174}