Skip to content

Commit 5c8117d

Browse files
committed
Expose LightningBalance/BalanceDetails newtypes
1 parent 299620e commit 5c8117d

File tree

3 files changed

+245
-0
lines changed

3 files changed

+245
-0
lines changed

bindings/ldk_node.udl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ interface LDKNode {
9494
PaymentDetails? payment([ByRef]PaymentHash payment_hash);
9595
[Throws=NodeError]
9696
void remove_payment([ByRef]PaymentHash payment_hash);
97+
BalanceDetails list_balances();
9798
sequence<PaymentDetails> list_payments();
9899
sequence<PeerDetails> list_peers();
99100
sequence<ChannelDetails> list_channels();
@@ -266,6 +267,21 @@ dictionary PeerDetails {
266267
boolean is_connected;
267268
};
268269

270+
[Enum]
271+
interface LightningBalance {
272+
ClaimableOnChannelClose ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis );
273+
ClaimableAwaitingConfirmations ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis, u32 confirmation_height );
274+
ContentiousClaimable ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis, u32 timeout_height, PaymentHash payment_hash, PaymentPreimage payment_preimage );
275+
MaybeTimeoutClaimableHTLC ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis, u32 claimable_height, PaymentHash payment_hash);
276+
MaybePreimageClaimableHTLC ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis, u32 expiry_height, PaymentHash payment_hash);
277+
CounterpartyRevokedOutputClaimable ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis );
278+
};
279+
280+
dictionary BalanceDetails {
281+
u64 total_lightning_balance_sats;
282+
sequence<LightningBalance> lightning_balances;
283+
};
284+
269285
interface ChannelConfig {
270286
constructor();
271287
u32 forwarding_fee_proportional_millionths();

src/balance.rs

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
use bitcoin::secp256k1::PublicKey;
2+
use lightning::chain::channelmonitor::Balance as LdkBalance;
3+
use lightning::ln::{ChannelId, PaymentHash, PaymentPreimage};
4+
5+
/// Details of the known available balances returned by [`Node::list_balances`].
6+
///
7+
/// [`Node::list_balances`]: crate::Node::list_balances
8+
#[derive(Debug, Clone)]
9+
pub struct BalanceDetails {
10+
/// The total balance that we would be able to claim across all our Lightning channels.
11+
///
12+
/// Note this excludes balances that we are unsure if we are able to claim (e.g., as we are
13+
/// waiting for a preimage or for a timeout to expire). These balances will however be included
14+
/// as [`MaybePreimageClaimableHTLC`] and
15+
/// [`MaybeTimeoutClaimableHTLC`] in [`lightning_balances`].
16+
///
17+
/// [`MaybePreimageClaimableHTLC`]: LightningBalance::MaybePreimageClaimableHTLC
18+
/// [`MaybeTimeoutClaimableHTLC`]: LightningBalance::MaybeTimeoutClaimableHTLC
19+
/// [`lightning_balances`]: Self::lightning_balances
20+
pub total_lightning_balance_sats: u64,
21+
/// A detailed list of all known Lightning balances that would be claimable on channel closure.
22+
///
23+
/// Note that less than the listed amounts are spendable over lightning as further reserve
24+
/// restrictions apply. Please refer to [`ChannelDetails::outbound_capacity_msat`] and
25+
/// [`ChannelDetails::next_outbound_htlc_limit_msat`] as returned by [`Node::list_channels`]
26+
/// for a better approximation of the spendable amounts.
27+
///
28+
/// [`ChannelDetails::outbound_capacity_msat`]: crate::ChannelDetails::outbound_capacity_msat
29+
/// [`ChannelDetails::next_outbound_htlc_limit_msat`]: crate::ChannelDetails::next_outbound_htlc_limit_msat
30+
/// [`Node::list_channels`]: crate::Node::list_channels
31+
pub lightning_balances: Vec<LightningBalance>,
32+
}
33+
34+
/// Details about the status of a known Lightning balance.
35+
#[derive(Debug, Clone)]
36+
pub enum LightningBalance {
37+
/// The channel is not yet closed (or the commitment or closing transaction has not yet
38+
/// appeared in a block). The given balance is claimable (less on-chain fees) if the channel is
39+
/// force-closed now.
40+
ClaimableOnChannelClose {
41+
/// The identifier of the channel this balance belongs to.
42+
channel_id: ChannelId,
43+
/// The identifier of our channel counterparty.
44+
counterparty_node_id: PublicKey,
45+
/// The amount available to claim, in satoshis, excluding the on-chain fees which will be
46+
/// required to do so.
47+
amount_satoshis: u64,
48+
},
49+
/// The channel has been closed, and the given balance is ours but awaiting confirmations until
50+
/// we consider it spendable.
51+
ClaimableAwaitingConfirmations {
52+
/// The identifier of the channel this balance belongs to.
53+
channel_id: ChannelId,
54+
/// The identifier of our channel counterparty.
55+
counterparty_node_id: PublicKey,
56+
/// The amount available to claim, in satoshis, possibly excluding the on-chain fees which
57+
/// were spent in broadcasting the transaction.
58+
amount_satoshis: u64,
59+
/// The height at which an [`Event::SpendableOutputs`] event will be generated for this
60+
/// amount.
61+
///
62+
/// [`Event::SpendableOutputs`]: lightning::events::Event::SpendableOutputs
63+
confirmation_height: u32,
64+
},
65+
/// The channel has been closed, and the given balance should be ours but awaiting spending
66+
/// transaction confirmation. If the spending transaction does not confirm in time, it is
67+
/// possible our counterparty can take the funds by broadcasting an HTLC timeout on-chain.
68+
///
69+
/// Once the spending transaction confirms, before it has reached enough confirmations to be
70+
/// considered safe from chain reorganizations, the balance will instead be provided via
71+
/// [`LightningBalance::ClaimableAwaitingConfirmations`].
72+
ContentiousClaimable {
73+
/// The identifier of the channel this balance belongs to.
74+
channel_id: ChannelId,
75+
/// The identifier of our channel counterparty.
76+
counterparty_node_id: PublicKey,
77+
/// The amount available to claim, in satoshis, excluding the on-chain fees which will be
78+
/// required to do so.
79+
amount_satoshis: u64,
80+
/// The height at which the counterparty may be able to claim the balance if we have not
81+
/// done so.
82+
timeout_height: u32,
83+
/// The payment hash that locks this HTLC.
84+
payment_hash: PaymentHash,
85+
/// The preimage that can be used to claim this HTLC.
86+
payment_preimage: PaymentPreimage,
87+
},
88+
/// HTLCs which we sent to our counterparty which are claimable after a timeout (less on-chain
89+
/// fees) if the counterparty does not know the preimage for the HTLCs. These are somewhat
90+
/// likely to be claimed by our counterparty before we do.
91+
MaybeTimeoutClaimableHTLC {
92+
/// The identifier of the channel this balance belongs to.
93+
channel_id: ChannelId,
94+
/// The identifier of our channel counterparty.
95+
counterparty_node_id: PublicKey,
96+
/// The amount potentially available to claim, in satoshis, excluding the on-chain fees
97+
/// which will be required to do so.
98+
amount_satoshis: u64,
99+
/// The height at which we will be able to claim the balance if our counterparty has not
100+
/// done so.
101+
claimable_height: u32,
102+
/// The payment hash whose preimage our counterparty needs to claim this HTLC.
103+
payment_hash: PaymentHash,
104+
},
105+
/// HTLCs which we received from our counterparty which are claimable with a preimage which we
106+
/// do not currently have. This will only be claimable if we receive the preimage from the node
107+
/// to which we forwarded this HTLC before the timeout.
108+
MaybePreimageClaimableHTLC {
109+
/// The identifier of the channel this balance belongs to.
110+
channel_id: ChannelId,
111+
/// The identifier of our channel counterparty.
112+
counterparty_node_id: PublicKey,
113+
/// The amount potentially available to claim, in satoshis, excluding the on-chain fees
114+
/// which will be required to do so.
115+
amount_satoshis: u64,
116+
/// The height at which our counterparty will be able to claim the balance if we have not
117+
/// yet received the preimage and claimed it ourselves.
118+
expiry_height: u32,
119+
/// The payment hash whose preimage we need to claim this HTLC.
120+
payment_hash: PaymentHash,
121+
},
122+
/// The channel has been closed, and our counterparty broadcasted a revoked commitment
123+
/// transaction.
124+
///
125+
/// Thus, we're able to claim all outputs in the commitment transaction, one of which has the
126+
/// following amount.
127+
CounterpartyRevokedOutputClaimable {
128+
/// The identifier of the channel this balance belongs to.
129+
channel_id: ChannelId,
130+
/// The identifier of our channel counterparty.
131+
counterparty_node_id: PublicKey,
132+
/// The amount, in satoshis, of the output which we can claim.
133+
amount_satoshis: u64,
134+
},
135+
}
136+
137+
impl LightningBalance {
138+
pub(crate) fn from_ldk_balance(
139+
channel_id: ChannelId, counterparty_node_id: PublicKey, balance: LdkBalance,
140+
) -> Self {
141+
match balance {
142+
LdkBalance::ClaimableOnChannelClose { amount_satoshis } => {
143+
Self::ClaimableOnChannelClose { channel_id, counterparty_node_id, amount_satoshis }
144+
}
145+
LdkBalance::ClaimableAwaitingConfirmations { amount_satoshis, confirmation_height } => {
146+
Self::ClaimableAwaitingConfirmations {
147+
channel_id,
148+
counterparty_node_id,
149+
amount_satoshis,
150+
confirmation_height,
151+
}
152+
}
153+
LdkBalance::ContentiousClaimable {
154+
amount_satoshis,
155+
timeout_height,
156+
payment_hash,
157+
payment_preimage,
158+
} => Self::ContentiousClaimable {
159+
channel_id,
160+
counterparty_node_id,
161+
amount_satoshis,
162+
timeout_height,
163+
payment_hash,
164+
payment_preimage,
165+
},
166+
LdkBalance::MaybeTimeoutClaimableHTLC {
167+
amount_satoshis,
168+
claimable_height,
169+
payment_hash,
170+
} => Self::MaybeTimeoutClaimableHTLC {
171+
channel_id,
172+
counterparty_node_id,
173+
amount_satoshis,
174+
claimable_height,
175+
payment_hash,
176+
},
177+
LdkBalance::MaybePreimageClaimableHTLC {
178+
amount_satoshis,
179+
expiry_height,
180+
payment_hash,
181+
} => Self::MaybePreimageClaimableHTLC {
182+
channel_id,
183+
counterparty_node_id,
184+
amount_satoshis,
185+
expiry_height,
186+
payment_hash,
187+
},
188+
LdkBalance::CounterpartyRevokedOutputClaimable { amount_satoshis } => {
189+
Self::CounterpartyRevokedOutputClaimable {
190+
channel_id,
191+
counterparty_node_id,
192+
amount_satoshis,
193+
}
194+
}
195+
}
196+
}
197+
}

src/lib.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
#![allow(ellipsis_inclusive_range_patterns)]
7676
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
7777

78+
mod balance;
7879
mod builder;
7980
mod error;
8081
mod event;
@@ -99,6 +100,7 @@ pub use bitcoin;
99100
pub use lightning;
100101
pub use lightning_invoice;
101102

103+
pub use balance::{BalanceDetails, LightningBalance};
102104
pub use error::Error as NodeError;
103105
use error::Error;
104106

@@ -1744,6 +1746,36 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
17441746
self.payment_store.remove(&payment_hash)
17451747
}
17461748

1749+
/// Retrieves an overview of all known balances.
1750+
pub fn list_balances(&self) -> BalanceDetails {
1751+
let mut total_lightning_balance_sats = 0;
1752+
let mut lightning_balances = Vec::new();
1753+
for funding_txo in self.chain_monitor.list_monitors() {
1754+
match self.chain_monitor.get_monitor(funding_txo) {
1755+
Ok(monitor) => {
1756+
// TODO: Switch to `channel_id` with LDK 0.0.122: let channel_id = monitor.channel_id();
1757+
let channel_id = funding_txo.to_channel_id();
1758+
// unwrap safety: `get_counterparty_node_id` will always be `Some` after 0.0.110 and
1759+
// LDK Node 0.1 depended on 0.0.115 already.
1760+
let counterparty_node_id = monitor.get_counterparty_node_id().unwrap();
1761+
for ldk_balance in monitor.get_claimable_balances() {
1762+
total_lightning_balance_sats += ldk_balance.claimable_amount_satoshis();
1763+
lightning_balances.push(LightningBalance::from_ldk_balance(
1764+
channel_id,
1765+
counterparty_node_id,
1766+
ldk_balance,
1767+
));
1768+
}
1769+
}
1770+
Err(()) => {
1771+
continue;
1772+
}
1773+
}
1774+
}
1775+
1776+
BalanceDetails { total_lightning_balance_sats, lightning_balances }
1777+
}
1778+
17471779
/// Retrieves all payments that match the given predicate.
17481780
///
17491781
/// For example, you could retrieve all stored outbound payments as follows:

0 commit comments

Comments
 (0)