Skip to content

Commit 5506f23

Browse files
committed
Expose list_{channels, peers} to bindings
1 parent c04f28f commit 5506f23

File tree

5 files changed

+233
-6
lines changed

5 files changed

+233
-6
lines changed

bindings/ldk_node.udl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ interface Node {
5151
[Throws=NodeError]
5252
Invoice receive_variable_amount_payment([ByRef]string description, u32 expiry_secs);
5353
PaymentInfo? payment_info([ByRef]PaymentHash payment_hash);
54+
sequence<PeerDetails> list_peers();
55+
sequence<ChannelDetails> list_channels();
5456
};
5557

5658
[Error]
@@ -67,6 +69,7 @@ enum NodeError {
6769
"NonUniquePaymentHash",
6870
"InvalidAmount",
6971
"InvalidInvoice",
72+
"InvalidTxid",
7073
"InvoiceCreationFailed",
7174
"InsufficientFunds",
7275
"PaymentFailed",
@@ -110,6 +113,39 @@ dictionary PaymentInfo {
110113
PaymentStatus status;
111114
};
112115

116+
dictionary OutPoint {
117+
Txid txid;
118+
u32 vout;
119+
};
120+
121+
dictionary ChannelDetails {
122+
ChannelId channel_id;
123+
PublicKey counterparty_node_id;
124+
OutPoint? funding_txo;
125+
u64? short_channel_id;
126+
u64? outbound_scid_alias;
127+
u64? inbound_scid_alias;
128+
u64 channel_value_satoshis;
129+
u64? unspendable_punishment_reserve;
130+
UserChannelId user_channel_id;
131+
u64 balance_msat;
132+
u64 outbound_capacity_msat;
133+
u64 inbound_capacity_msat;
134+
u32? confirmations_required;
135+
u32? confirmations;
136+
boolean is_outbound;
137+
boolean is_channel_ready;
138+
boolean is_usable;
139+
boolean is_public;
140+
u16? cltv_expiry_delta;
141+
};
142+
143+
dictionary PeerDetails {
144+
PublicKey node_id;
145+
SocketAddr address;
146+
boolean is_connected;
147+
};
148+
113149
[Custom]
114150
typedef string SocketAddr;
115151

@@ -139,3 +175,6 @@ typedef string UserChannelId;
139175

140176
[Custom]
141177
typedef string Network;
178+
179+
[Custom]
180+
typedef string Txid;

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ pub enum Error {
2727
InvalidAmount,
2828
/// The given invoice is invalid.
2929
InvalidInvoice,
30+
/// The given transaction identifier is invalid
31+
InvalidTxid,
3032
/// Invoice creation failed.
3133
InvoiceCreationFailed,
3234
/// There are insufficient funds to complete the given operation.
@@ -82,6 +84,7 @@ impl fmt::Display for Error {
8284
Self::WalletOperationFailed => write!(f, "Failed to conduct wallet operation."),
8385
Self::WalletSigningFailed => write!(f, "Failed to sign given transaction."),
8486
Self::TxSyncFailed => write!(f, "Failed to sync transactions."),
87+
Self::InvalidTxid => write!(f, "The given transaction identifier is invalid."),
8588
}
8689
}
8790
}

src/lib.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,17 @@ use types::{
104104
ChainMonitor, ChannelManager, GossipSync, KeysManager, Network, NetworkGraph, OnionMessenger,
105105
PeerManager, Scorer,
106106
};
107-
pub use types::{ChannelId, UserChannelId};
107+
pub use types::{
108+
ChannelDetails, ChannelId, PeerDetails, UserChannelId
109+
};
108110
use wallet::Wallet;
109111

110112
use logger::{log_error, log_info, FilesystemLogger, Logger};
111113

112114
use lightning::chain::keysinterface::EntropySource;
113115
use lightning::chain::{chainmonitor, BestBlock, Confirm, Watch};
114116
use lightning::ln::channelmanager::{
115-
self, ChainParameters, ChannelDetails, ChannelManagerReadArgs, PaymentId, Retry,
117+
self, ChainParameters, ChannelManagerReadArgs, PaymentId, Retry,
116118
};
117119
use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler};
118120
use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
@@ -139,7 +141,7 @@ use bdk::template::Bip84;
139141
use bitcoin::hashes::sha256::Hash as Sha256;
140142
use bitcoin::hashes::Hash;
141143
use bitcoin::secp256k1::PublicKey;
142-
use bitcoin::{Address, BlockHash};
144+
use bitcoin::{Address, BlockHash, OutPoint, Txid};
143145

144146
use rand::Rng;
145147

@@ -853,7 +855,7 @@ impl Node {
853855

854856
/// Retrieve a list of known channels.
855857
pub fn list_channels(&self) -> Vec<ChannelDetails> {
856-
self.channel_manager.list_channels()
858+
self.channel_manager.list_channels().into_iter().map(|c| c.into()).collect()
857859
}
858860

859861
/// Connect to a node on the peer-to-peer network.
@@ -1341,6 +1343,21 @@ impl Node {
13411343
pub fn payment_info(&self, payment_hash: &PaymentHash) -> Option<PaymentInfo> {
13421344
self.payment_store.get(payment_hash)
13431345
}
1346+
1347+
/// Retrieves a list of known peers.
1348+
pub fn list_peers(&self) -> Vec<PeerDetails> {
1349+
let active_connected_peers: Vec<PublicKey> =
1350+
self.peer_manager.get_peer_node_ids().iter().map(|p| p.0).collect();
1351+
self.peer_store
1352+
.list_peers()
1353+
.iter()
1354+
.map(|p| PeerDetails {
1355+
node_id: p.pubkey,
1356+
address: p.address,
1357+
is_connected: active_connected_peers.contains(&p.pubkey),
1358+
})
1359+
.collect()
1360+
}
13441361
}
13451362

13461363
async fn connect_peer_if_necessary(

src/test/functional_tests.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ fn channel_full_cycle() {
3737
.connect_open_channel(&node_b.node_id(), &node_b.listening_address().unwrap(), 50000, true)
3838
.unwrap();
3939

40+
assert_eq!(
41+
node_a.list_peers().iter().map(|p| p.node_id).collect::<Vec<_>>(),
42+
[node_b.node_id()]
43+
);
44+
4045
let funding_txo = loop {
4146
let details = node_a.list_channels();
4247

@@ -153,7 +158,7 @@ fn channel_full_cycle() {
153158
expect_event!(node_a, ChannelClosed);
154159
expect_event!(node_b, ChannelClosed);
155160

156-
wait_for_outpoint_spend(&electrsd, funding_txo.into_bitcoin_outpoint());
161+
wait_for_outpoint_spend(&electrsd, funding_txo);
157162

158163
generate_blocks_and_wait(&bitcoind, &electrsd, 1);
159164
node_a.sync_wallets().unwrap();

src/types.rs

Lines changed: 164 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::UniffiCustomTypeConverter;
77

88
use lightning::chain::chainmonitor;
99
use lightning::chain::keysinterface::InMemorySigner;
10+
use lightning::ln::channelmanager::ChannelDetails as LdkChannelDetails;
1011
use lightning::ln::peer_handler::IgnoringMessageHandler;
1112
use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
1213
use lightning::routing::gossip;
@@ -22,7 +23,7 @@ use lightning_transaction_sync::EsploraSyncClient;
2223
use bitcoin::hashes::sha256::Hash as Sha256;
2324
use bitcoin::hashes::Hash;
2425
use bitcoin::secp256k1::PublicKey;
25-
use bitcoin::Address;
26+
use bitcoin::{Address, OutPoint, Txid};
2627

2728
use std::convert::TryInto;
2829
use std::default::Default;
@@ -339,3 +340,165 @@ impl UniffiCustomTypeConverter for Network {
339340
obj.0.to_string()
340341
}
341342
}
343+
344+
/// Details of a channel as returned by [`Node::list_channels`].
345+
///
346+
/// [`Node::list_channels`]: [`crate::Node::list_channels`]
347+
pub struct ChannelDetails {
348+
/// The channel's ID (prior to funding transaction generation, this is a random 32 bytes,
349+
/// thereafter this is the transaction ID of the funding transaction XOR the funding transaction
350+
/// output).
351+
///
352+
/// Note that this means this value is *not* persistent - it can change once during the
353+
/// lifetime of the channel.
354+
pub channel_id: ChannelId,
355+
/// The `node_id` of our channel's counterparty.
356+
pub counterparty_node_id: PublicKey,
357+
/// The channel's funding transaction output, if we've negotiated the funding transaction with
358+
/// our counterparty already.
359+
pub funding_txo: Option<OutPoint>,
360+
/// Position of the funding transaction on-chain. `None` unless the funding transaction has been
361+
/// confirmed and the channel is fully opened.
362+
///
363+
/// Note that if [`inbound_scid_alias`] is set, it must be used for invoices and inbound
364+
/// payments instead of this.
365+
///
366+
/// For channels with [`confirmations_required`] set to `Some(0)`, [`outbound_scid_alias`] may
367+
/// be used in place of this in outbound routes.
368+
///
369+
/// [`inbound_scid_alias`]: Self::inbound_scid_alias
370+
/// [`outbound_scid_alias`]: Self::outbound_scid_alias
371+
/// [`confirmations_required`]: Self::confirmations_required
372+
pub short_channel_id: Option<u64>,
373+
/// An optional [`short_channel_id`] alias for this channel, randomly generated by us and
374+
/// usable in place of [`short_channel_id`] to reference the channel in outbound routes when
375+
/// the channel has not yet been confirmed (as long as [`confirmations_required`] is
376+
/// `Some(0)`).
377+
///
378+
/// This will be `None` as long as the channel is not available for routing outbound payments.
379+
///
380+
/// [`short_channel_id`]: Self::short_channel_id
381+
/// [`confirmations_required`]: Self::confirmations_required
382+
pub outbound_scid_alias: Option<u64>,
383+
/// An optional [`short_channel_id`] alias for this channel, randomly generated by our
384+
/// counterparty and usable in place of [`short_channel_id`] in invoice route hints. Our
385+
/// counterparty will recognize the alias provided here in place of the [`short_channel_id`]
386+
/// when they see a payment to be routed to us.
387+
///
388+
/// Our counterparty may choose to rotate this value at any time, though will always recognize
389+
/// previous values for inbound payment forwarding.
390+
///
391+
/// [`short_channel_id`]: Self::short_channel_id
392+
pub inbound_scid_alias: Option<u64>,
393+
/// The value, in satoshis, of this channel as appears in the funding output.
394+
pub channel_value_satoshis: u64,
395+
/// The value, in satoshis, that must always be held in the channel for us. This value ensures
396+
/// that if we broadcast a revoked state, our counterparty can punish us by claiming at least
397+
/// this value on chain.
398+
///
399+
/// This value is not included in [`outbound_capacity_msat`] as it can never be spent.
400+
///
401+
/// This value will be `None` for outbound channels until the counterparty accepts the channel.
402+
///
403+
/// [`outbound_capacity_msat`]: Self::outbound_capacity_msat
404+
pub unspendable_punishment_reserve: Option<u64>,
405+
/// The local `user_channel_id` of this channel.
406+
pub user_channel_id: UserChannelId,
407+
/// Total balance of the channel. This is the amount that will be returned to the user if the
408+
/// channel is closed.
409+
///
410+
/// The value is not exact, due to potential in-flight and fee-rate changes. Therefore, exactly
411+
/// this amount is likely irrecoverable on close.
412+
pub balance_msat: u64,
413+
/// Available outbound capacity for sending HTLCs to the remote peer.
414+
///
415+
/// The amount does not include any pending HTLCs which are not yet resolved (and, thus, whose
416+
/// balance is not available for inclusion in new outbound HTLCs). This further does not include
417+
/// any pending outgoing HTLCs which are awaiting some other resolution to be sent.
418+
pub outbound_capacity_msat: u64,
419+
/// Available outbound capacity for sending HTLCs to the remote peer.
420+
///
421+
/// The amount does not include any pending HTLCs which are not yet resolved
422+
/// (and, thus, whose balance is not available for inclusion in new inbound HTLCs). This further
423+
/// does not include any pending outgoing HTLCs which are awaiting some other resolution to be
424+
/// sent.
425+
pub inbound_capacity_msat: u64,
426+
/// The number of required confirmations on the funding transactions before the funding is
427+
/// considered "locked". The amount is selected by the channel fundee.
428+
///
429+
/// The value will be `None` for outbound channels until the counterparty accepts the channel.
430+
pub confirmations_required: Option<u32>,
431+
/// The current number of confirmations on the funding transaction.
432+
pub confirmations: Option<u32>,
433+
/// Returns `true` if the channel was initiated (and therefore funded) by us.
434+
pub is_outbound: bool,
435+
/// Returns `true` if the channel is confirmed, both parties have exchanged `channel_ready`
436+
/// messages, and the channel is not currently being shut down. Both parties exchange
437+
/// `channel_ready` messages upon independently verifying that the required confirmations count
438+
/// provided by `confirmations_required` has been reached.
439+
pub is_channel_ready: bool,
440+
/// Returns `true` if the channel is (a) confirmed and `channel_ready` has been exchanged,
441+
/// (b) the peer is connected, and (c) the channel is not currently negotiating shutdown.
442+
///
443+
/// This is a strict superset of `is_channel_ready`.
444+
pub is_usable: bool,
445+
/// Returns `true` if this channel is (or will be) publicly-announced
446+
pub is_public: bool,
447+
/// The difference in the CLTV value between incoming HTLCs and an outbound HTLC forwarded over
448+
/// the channel.
449+
pub cltv_expiry_delta: Option<u16>,
450+
}
451+
452+
impl From<LdkChannelDetails> for ChannelDetails {
453+
fn from(value: LdkChannelDetails) -> Self {
454+
ChannelDetails {
455+
channel_id: ChannelId(value.channel_id),
456+
counterparty_node_id: value.counterparty.node_id,
457+
funding_txo: value.funding_txo.and_then(|o| Some(o.into_bitcoin_outpoint())),
458+
short_channel_id: value.short_channel_id,
459+
outbound_scid_alias: value.outbound_scid_alias,
460+
inbound_scid_alias: value.inbound_scid_alias,
461+
channel_value_satoshis: value.channel_value_satoshis,
462+
unspendable_punishment_reserve: value.unspendable_punishment_reserve,
463+
user_channel_id: UserChannelId(value.user_channel_id),
464+
balance_msat: value.balance_msat,
465+
outbound_capacity_msat: value.outbound_capacity_msat,
466+
inbound_capacity_msat: value.inbound_capacity_msat,
467+
confirmations_required: value.confirmations_required,
468+
confirmations: value.confirmations,
469+
is_outbound: value.is_outbound,
470+
is_channel_ready: value.is_channel_ready,
471+
is_usable: value.is_usable,
472+
is_public: value.is_public,
473+
cltv_expiry_delta: value.config.and_then(|c| Some(c.cltv_expiry_delta)),
474+
}
475+
}
476+
}
477+
478+
impl UniffiCustomTypeConverter for Txid {
479+
type Builtin = String;
480+
481+
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
482+
if let Ok(txid) = Txid::from_str(&val) {
483+
return Ok(txid);
484+
}
485+
486+
Err(Error::InvalidTxid.into())
487+
}
488+
489+
fn from_custom(obj: Self) -> Self::Builtin {
490+
obj.to_string()
491+
}
492+
}
493+
494+
/// Details of a known lightning peer as returned by [`Node::list_peers`].
495+
///
496+
/// [`Node::list_peers`]: [`crate::Node::list_peers`]
497+
pub struct PeerDetails {
498+
/// Our peer's node ID.
499+
pub node_id: PublicKey,
500+
/// The IP address and TCP port of the peer.
501+
pub address: SocketAddr,
502+
/// Indicates whether or not the user is currently has an active connection with the peer.
503+
pub is_connected: bool,
504+
}

0 commit comments

Comments
 (0)