diff --git a/src/client.rs b/src/client.rs index b87bfaaa..b0e72f68 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,3 +1,5 @@ +use bitcoin::p2p::address::AddrV2; +use bitcoin::p2p::ServiceFlags; use bitcoin::{Amount, Transaction, Wtxid}; use bitcoin::{BlockHash, FeeRate}; use tokio::sync::mpsc; @@ -166,6 +168,20 @@ impl Requester { Ok(FeeRate::from_sat_per_kwu(fee_rate)) } + /// Get the current peer connections. + /// + /// # Errors + /// + /// If the node has stopped running. + pub async fn peer_info(&self) -> Result, ClientError> { + let (tx, rx) = tokio::sync::oneshot::channel::>(); + let request = ClientRequest::new((), tx); + self.ntx + .send(ClientMessage::GetPeerInfo(request)) + .map_err(|_| ClientError::SendError)?; + rx.await.map_err(|_| ClientError::RecvError) + } + /// Starting after the configured checkpoint, re-emit all block filters. /// /// # Errors diff --git a/src/messages.rs b/src/messages.rs index ec53868f..d8812834 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -1,6 +1,8 @@ use std::collections::BTreeMap; use std::ops::Div; +use bitcoin::p2p::address::AddrV2; +use bitcoin::p2p::ServiceFlags; use bitcoin::{ block::Header, p2p::message_network::RejectReason, BlockHash, FeeRate, Transaction, Wtxid, }; @@ -151,6 +153,8 @@ pub(crate) enum ClientMessage { AddPeer(TrustedPeer), /// Request the broadcast minimum fee rate. GetBroadcastMinFeeRate(ClientRequest<(), FeeRate>), + /// Get info on connections + GetPeerInfo(ClientRequest<(), Vec<(AddrV2, ServiceFlags)>>), /// Send an empty message to see if the node is running. NoOp, } diff --git a/src/network/peer_map.rs b/src/network/peer_map.rs index 7a5316cf..dbc6f442 100644 --- a/src/network/peer_map.rs +++ b/src/network/peer_map.rs @@ -190,6 +190,13 @@ impl PeerMap { .unwrap_or(FeeRate::BROADCAST_MIN) } + pub fn peer_info(&self) -> Vec<(AddrV2, ServiceFlags)> { + self.map + .values() + .map(|peer| (peer.record.network_addr().0, peer.record.service_flags())) + .collect() + } + // Send a message to the specified peer pub async fn send_message(&self, nonce: PeerId, message: MainThreadMessage) { if let Some(peer) = self.map.get(&nonce) { diff --git a/src/node.rs b/src/node.rs index 2a70cc66..fcca2e8a 100644 --- a/src/node.rs +++ b/src/node.rs @@ -258,6 +258,14 @@ impl Node { self.dialog.send_warning(Warning::ChannelDropped); }; } + ClientMessage::GetPeerInfo(request) => { + let (_, oneshot) = request.into_values(); + let peers = self.peer_map.peer_info(); + let send_result = oneshot.send(peers); + if send_result.is_err() { + self.dialog.send_warning(Warning::ChannelDropped); + }; + } ClientMessage::NoOp => (), } } diff --git a/tests/core.rs b/tests/core.rs index 525c4ef5..5d62e984 100644 --- a/tests/core.rs +++ b/tests/core.rs @@ -250,6 +250,8 @@ async fn various_client_methods() { let _ = requester.broadcast_min_feerate().await.unwrap(); let cp = requester.chain_tip().await.unwrap(); assert_eq!(cp.hash, best); + let peers = requester.peer_info().await.unwrap(); + assert_eq!(peers.len(), 1); assert!(requester.is_running()); requester.shutdown().unwrap(); rpc.stop().unwrap();