From 3dfb064d2f47d4888638ad6f97a4f5023def58eb Mon Sep 17 00:00:00 2001 From: Jurvis Tan Date: Fri, 24 Mar 2023 22:37:42 -0700 Subject: [PATCH 1/3] Expose list_peers to bindings --- bindings/ldk_node.udl | 1 + src/lib.rs | 5 +++++ src/test/functional_tests.rs | 2 ++ 3 files changed, 8 insertions(+) diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index 89205fa44..4cba8731a 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -51,6 +51,7 @@ interface Node { [Throws=NodeError] Invoice receive_variable_amount_payment([ByRef]string description, u32 expiry_secs); PaymentInfo? payment_info([ByRef]PaymentHash payment_hash); + sequence list_peers(); }; [Error] diff --git a/src/lib.rs b/src/lib.rs index f71efac81..e55c0727c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1341,6 +1341,11 @@ impl Node { pub fn payment_info(&self, payment_hash: &PaymentHash) -> Option { self.payment_store.get(payment_hash) } + + /// List node's connected peers. + pub fn list_peers(&self) -> Vec { + self.peer_manager.get_peer_node_ids().iter().map(|(pubkey, _)| *pubkey).collect::>() + } } async fn connect_peer_if_necessary( diff --git a/src/test/functional_tests.rs b/src/test/functional_tests.rs index 5175a524b..a50ba23ae 100644 --- a/src/test/functional_tests.rs +++ b/src/test/functional_tests.rs @@ -37,6 +37,8 @@ fn channel_full_cycle() { .connect_open_channel(&node_b.node_id(), &node_b.listening_address().unwrap(), 50000, true) .unwrap(); + assert_eq!(node_a.list_peers(), [node_b.node_id()]); + let funding_txo = loop { let details = node_a.list_channels(); From 17a91d3298bf5b0d6e99b3f21391df0031fbac5b Mon Sep 17 00:00:00 2001 From: Jurvis Tan Date: Mon, 27 Mar 2023 16:43:18 -0700 Subject: [PATCH 2/3] Expose list_channels in bindings --- bindings/ldk_node.udl | 24 ++++++++++ src/lib.rs | 8 ++-- src/test/functional_tests.rs | 2 +- src/types.rs | 92 ++++++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 5 deletions(-) diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index 4cba8731a..6e44319fa 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -52,6 +52,7 @@ interface Node { Invoice receive_variable_amount_payment([ByRef]string description, u32 expiry_secs); PaymentInfo? payment_info([ByRef]PaymentHash payment_hash); sequence list_peers(); + sequence list_channels(); }; [Error] @@ -111,6 +112,29 @@ dictionary PaymentInfo { PaymentStatus status; }; +dictionary OutPoint { + string txid; + u16 index; +}; + +dictionary ChannelDetails { + ChannelId channel_id; + PublicKey counterparty; + OutPoint? funding_txo; + u64? short_channel_id; + u64 channel_value_satoshis; + u64 balance_msat; + u64 outbound_capacity_msat; + u64 inbound_capacity_msat; + u32? confirmations_required; + u32? confirmations; + boolean is_outbound; + boolean is_channel_ready; + boolean is_usable; + boolean is_public; + u16? cltv_expiry_delta; +}; + [Custom] typedef string SocketAddr; diff --git a/src/lib.rs b/src/lib.rs index e55c0727c..af4a6dbe9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,8 +101,8 @@ use payment_store::PaymentInfoStorage; pub use payment_store::{PaymentDirection, PaymentInfo, PaymentStatus}; use peer_store::{PeerInfo, PeerInfoStorage}; use types::{ - ChainMonitor, ChannelManager, GossipSync, KeysManager, Network, NetworkGraph, OnionMessenger, - PeerManager, Scorer, + ChainMonitor, ChannelDetails, ChannelManager, GossipSync, KeysManager, Network, NetworkGraph, + OnionMessenger, OutPoint, PeerManager, Scorer, }; pub use types::{ChannelId, UserChannelId}; use wallet::Wallet; @@ -112,7 +112,7 @@ use logger::{log_error, log_info, FilesystemLogger, Logger}; use lightning::chain::keysinterface::EntropySource; use lightning::chain::{chainmonitor, BestBlock, Confirm, Watch}; use lightning::ln::channelmanager::{ - self, ChainParameters, ChannelDetails, ChannelManagerReadArgs, PaymentId, Retry, + self, ChainParameters, ChannelManagerReadArgs, PaymentId, Retry, }; use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler}; use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret}; @@ -853,7 +853,7 @@ impl Node { /// Retrieve a list of known channels. pub fn list_channels(&self) -> Vec { - self.channel_manager.list_channels() + self.channel_manager.list_channels().iter().map(|c| c.clone().into()).collect() } /// Connect to a node on the peer-to-peer network. diff --git a/src/test/functional_tests.rs b/src/test/functional_tests.rs index a50ba23ae..7fdfaaf3a 100644 --- a/src/test/functional_tests.rs +++ b/src/test/functional_tests.rs @@ -40,7 +40,7 @@ fn channel_full_cycle() { assert_eq!(node_a.list_peers(), [node_b.node_id()]); let funding_txo = loop { - let details = node_a.list_channels(); + let details = node_a.channel_manager.list_channels(); if details.is_empty() || details[0].funding_txo.is_none() { std::thread::sleep(Duration::from_secs(1)); diff --git a/src/types.rs b/src/types.rs index 22b5bcee4..b53f9fb6a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -7,6 +7,8 @@ use crate::UniffiCustomTypeConverter; use lightning::chain::chainmonitor; use lightning::chain::keysinterface::InMemorySigner; +use lightning::chain::transaction::OutPoint as LdkOutpoint; +use lightning::ln::channelmanager::ChannelDetails as LdkChannelDetails; use lightning::ln::peer_handler::IgnoringMessageHandler; use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret}; use lightning::routing::gossip; @@ -339,3 +341,93 @@ impl UniffiCustomTypeConverter for Network { obj.0.to_string() } } + +/// Details about the user's channel as returned by [`Node::list_channels`]. +/// +/// [`Node::list_channels`]: [`crate::Node::list_channels`] +pub struct ChannelDetails { + /// The channel's ID. + pub channel_id: ChannelId, + /// The `node_id` of our channel's counterparty. + pub counterparty: PublicKey, + /// Information about the channel's funding transaction output. `None `unless a funding + /// transaction has been successfully negotiated with the channel's counterparty. + pub funding_txo: Option, + /// Position of the funding transaction on-chain. `None` unless the funding transaction has been + /// confirmed and fully opened. + pub short_channel_id: Option, + /// The value, in satoshis, of this channel as appears in the funding output. + pub channel_value_satoshis: u64, + /// Total balance of the channel. It is the amount that will be returned to the user if the + /// channel is closed. The value is not exact, due to potential in-flight and fee-rate changes. + /// Therefore, exactly this amount is likely irrecoverable on close. + pub balance_msat: u64, + /// Available outbound capacity for sending HTLCs to the remote peer. The amount does not + /// include any pending HTLCs which are not yet resolved (and, thus, whose balance is not + /// available for inclusion in new outbound HTLCs). This further does not include any + /// pending outgoing HTLCs which are awaiting some other resolution to be sent. + pub outbound_capacity_msat: u64, + /// Available outbound capacity for sending HTLCs to the remote peer. The amount does not + /// include any pending HTLCs which are not yet resolved (and, thus, whose balance is not + /// available for inclusion in new inbound HTLCs). This further does not include any + /// pending outgoing HTLCs which are awaiting some other resolution to be sent. + pub inbound_capacity_msat: u64, + /// The number of required confirmations on the funding transactions before the funding is + /// considered "locked". The amount is selected by the channel fundee. + /// + /// The value will be `None` for outbound channels until the counterparty accepts the channel. + pub confirmations_required: Option, + /// The current number of confirmations on the funding transaction. + pub confirmations: Option, + /// Returns `True` if the channel was initiated (and therefore funded) by us. + pub is_outbound: bool, + /// Returns `True` if the channel is confirmed, both parties have exchanged `channel_ready` + /// messages, and the channel is not currently being shut down. Both parties exchange + /// `channel_ready` messages upon independently verifying that the required confirmations count + /// provided by `confirmations_required` has been reached. + pub is_channel_ready: bool, + /// Returns `True` if the channel is (a) confirmed and `channel_ready` has been exchanged, + /// (b) the peer is connected, and (c) the channel is not currently negotiating shutdown. + pub is_usable: bool, + /// Returns `True` if this channel is (or will be) publicly-announced + pub is_public: bool, + /// The difference in the CLTV value between incoming HTLCs and an outbound HTLC forwarded over + /// the channel. + pub cltv_expiry_delta: Option, +} + +impl From for ChannelDetails { + fn from(value: LdkChannelDetails) -> Self { + ChannelDetails { + channel_id: ChannelId(value.channel_id), + counterparty: value.counterparty.node_id, + funding_txo: value.funding_txo.and_then(|o| Some(o.into())), + short_channel_id: value.short_channel_id, + channel_value_satoshis: value.channel_value_satoshis, + balance_msat: value.balance_msat, + outbound_capacity_msat: value.outbound_capacity_msat, + inbound_capacity_msat: value.inbound_capacity_msat, + confirmations_required: value.confirmations_required, + confirmations: value.confirmations, + is_outbound: value.is_outbound, + is_channel_ready: value.is_channel_ready, + is_usable: value.is_usable, + is_public: value.is_public, + cltv_expiry_delta: value.config.and_then(|c| Some(c.cltv_expiry_delta)), + } + } +} + +/// Data structure that references and transaction output. +pub struct OutPoint { + /// The referenced transaction's txid. + pub txid: String, + /// The index of the referenced output in its transaction's vout. + pub index: u16, +} + +impl From for OutPoint { + fn from(value: LdkOutpoint) -> Self { + OutPoint { txid: value.txid.to_string(), index: value.index } + } +} From 542f71fa4e59a6e0f1cf73b5b0ea25902193b6e6 Mon Sep 17 00:00:00 2001 From: Jurvis Tan Date: Fri, 31 Mar 2023 11:23:14 -0700 Subject: [PATCH 3/3] Return connection state of peer in `list_peers` --- bindings/ldk_node.udl | 7 ++++++- src/lib.rs | 15 ++++++++++++--- src/test/functional_tests.rs | 5 ++++- src/types.rs | 10 ++++++++++ 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index 6e44319fa..426d63bbc 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -51,7 +51,7 @@ interface Node { [Throws=NodeError] Invoice receive_variable_amount_payment([ByRef]string description, u32 expiry_secs); PaymentInfo? payment_info([ByRef]PaymentHash payment_hash); - sequence list_peers(); + sequence list_peers(); sequence list_channels(); }; @@ -135,6 +135,11 @@ dictionary ChannelDetails { u16? cltv_expiry_delta; }; +dictionary PeerDetails { + PublicKey node_id; + boolean is_connected; +}; + [Custom] typedef string SocketAddr; diff --git a/src/lib.rs b/src/lib.rs index af4a6dbe9..83f6ad772 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,7 +102,7 @@ pub use payment_store::{PaymentDirection, PaymentInfo, PaymentStatus}; use peer_store::{PeerInfo, PeerInfoStorage}; use types::{ ChainMonitor, ChannelDetails, ChannelManager, GossipSync, KeysManager, Network, NetworkGraph, - OnionMessenger, OutPoint, PeerManager, Scorer, + OnionMessenger, OutPoint, PeerDetails, PeerManager, Scorer, }; pub use types::{ChannelId, UserChannelId}; use wallet::Wallet; @@ -1343,8 +1343,17 @@ impl Node { } /// List node's connected peers. - pub fn list_peers(&self) -> Vec { - self.peer_manager.get_peer_node_ids().iter().map(|(pubkey, _)| *pubkey).collect::>() + pub fn list_peers(&self) -> Vec { + let active_connected_peers: Vec = + self.peer_manager.get_peer_node_ids().iter().map(|p| p.0).collect(); + self.peer_store + .list_peers() + .iter() + .map(|p| PeerDetails { + node_id: p.pubkey, + is_connected: active_connected_peers.contains(&p.pubkey), + }) + .collect() } } diff --git a/src/test/functional_tests.rs b/src/test/functional_tests.rs index 7fdfaaf3a..aec77f30c 100644 --- a/src/test/functional_tests.rs +++ b/src/test/functional_tests.rs @@ -37,7 +37,10 @@ fn channel_full_cycle() { .connect_open_channel(&node_b.node_id(), &node_b.listening_address().unwrap(), 50000, true) .unwrap(); - assert_eq!(node_a.list_peers(), [node_b.node_id()]); + assert_eq!( + node_a.list_peers().iter().map(|p| p.node_id).collect::>(), + [node_b.node_id()] + ); let funding_txo = loop { let details = node_a.channel_manager.list_channels(); diff --git a/src/types.rs b/src/types.rs index b53f9fb6a..376c7f1b1 100644 --- a/src/types.rs +++ b/src/types.rs @@ -431,3 +431,13 @@ impl From for OutPoint { OutPoint { txid: value.txid.to_string(), index: value.index } } } + +/// Details about peers the user is connected to. As returned by [`Node::list_peers`]. +/// +/// [`Node::list_peers`]: [`crate::Node::list_peers`] +pub struct PeerDetails { + /// Our peer's node ID. + pub node_id: PublicKey, + /// Indicates whether or not the user is currently has an active connection with the peer. + pub is_connected: bool, +}