Skip to content

Commit a07375f

Browse files
committed
Expose LightningBalance/BalanceDetails newtypes
1 parent 980b14c commit a07375f

File tree

3 files changed

+230
-0
lines changed

3 files changed

+230
-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();
@@ -243,6 +244,21 @@ dictionary PeerDetails {
243244
boolean is_connected;
244245
};
245246

247+
[Enum]
248+
interface LightningBalance {
249+
ClaimableOnChannelClose ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis );
250+
ClaimableAwaitingConfirmations ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis, u32 confirmation_height );
251+
ContentiousClaimable ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis, u32 timeout_height, PaymentHash payment_hash, PaymentPreimage payment_preimage );
252+
MaybeTimeoutClaimableHTLC ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis, u32 claimable_height, PaymentHash payment_hash);
253+
MaybePreimageClaimableHTLC ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis, u32 expiry_height, PaymentHash payment_hash);
254+
CounterpartyRevokedOutputClaimable ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis );
255+
};
256+
257+
dictionary BalanceDetails {
258+
u64 total_lightning_balance_sats;
259+
sequence<LightningBalance> lightning_balances;
260+
};
261+
246262
interface ChannelConfig {
247263
constructor();
248264
u32 forwarding_fee_proportional_millionths();

src/balance.rs

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

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

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

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

0 commit comments

Comments
 (0)