use anyhow::Error;
use async_trait::async_trait;
use clap::Parser;
use tari_comms::PeerConnection;
use tari_core::base_node::state_machine_service::states::PeerMetadata;
use super::{CommandContext, HandleCommand};
use crate::{table::Table, utils::format_duration_basic};
#[derive(Debug, Parser)]
pub struct Args {}
#[async_trait]
impl HandleCommand<Args> for CommandContext {
async fn handle_command(&mut self, _: Args) -> Result<(), Error> {
self.list_connections().await
}
}
impl CommandContext {
async fn list_connections_print_table(&mut self, conns: &[PeerConnection]) {
let num_connections = conns.len();
let mut table = Table::new();
table.set_titles(vec![
"NodeId",
"Public Key",
"Address",
"Direction",
"Age",
"User Agent",
"Seed",
"Info",
]);
let peer_manager = self.comms.peer_manager();
let node_ids = conns
.iter()
.map(|conn| conn.peer_node_id())
.cloned()
.collect::<Vec<_>>();
let peers = match peer_manager.get_peers_by_node_ids(&node_ids).await {
Ok(val) => val,
Err(e) => {
println!("Error: Unexpected peer database error: {e}");
return;
},
};
if peers.len() != node_ids.len() {
println!("\nError: Peer manager returned fewer peers than requested\n");
}
for peer in peers {
let conn = match conns.iter().find(|conn| conn.peer_node_id() == &peer.node_id) {
None => continue,
Some(val) => val,
};
let chain_height = peer
.get_metadata(1)
.and_then(|v| bincode::deserialize::<PeerMetadata>(v).ok())
.map(|metadata| format!("height: {}", metadata.metadata.best_block_height()));
let is_seed = peer.is_seed();
let ua = peer.user_agent;
let rpc_sessions = self
.rpc_server
.get_num_active_sessions_for(peer.node_id.clone())
.await
.unwrap_or(0);
table.add_row(row![
peer.node_id,
peer.public_key,
conn.address(),
conn.direction(),
format_duration_basic(conn.age()),
{ if ua.is_empty() { "<unknown>" } else { ua.as_ref() } },
if is_seed { "SEED" } else { " " },
format!(
"{}hnd: {}, ss: {}, rpc: {}",
chain_height.map(|s| format!("{s}, ")).unwrap_or_default(),
conn.handle_count().saturating_sub(1),
conn.substream_count(),
rpc_sessions
),
]);
}
table.print_stdout();
println!("{num_connections} active connection(s)");
}
}
impl CommandContext {
pub async fn list_connections(&mut self) -> Result<(), Error> {
let conns = self.comms.connectivity().get_active_connections().await?;
let (mut nodes, mut clients) = conns
.into_iter()
.partition::<Vec<_>, _>(|a| a.peer_features().is_node());
nodes.sort_by(|a, b| a.peer_node_id().cmp(b.peer_node_id()));
clients.sort_by(|a, b| a.peer_node_id().cmp(b.peer_node_id()));
println!();
println!("Base Nodes");
println!("----------");
if nodes.is_empty() {
println!("No active node connections.");
} else {
println!();
self.list_connections_print_table(&nodes).await;
}
println!();
println!("Wallets");
println!("-------");
if clients.is_empty() {
println!("No active wallet connections.");
} else {
println!();
self.list_connections_print_table(&clients).await;
}
Ok(())
}
}