Skip to content

Commit eeb6861

Browse files
committed
Expose LightningBalance/BalanceDetails newtypes
1 parent 741e706 commit eeb6861

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
@@ -87,6 +87,7 @@ interface LDKNode {
8787
PaymentDetails? payment([ByRef]PaymentHash payment_hash);
8888
[Throws=NodeError]
8989
void remove_payment([ByRef]PaymentHash payment_hash);
90+
BalanceDetails list_balances();
9091
sequence<PaymentDetails> list_payments();
9192
sequence<PeerDetails> list_peers();
9293
sequence<ChannelDetails> list_channels();
@@ -227,6 +228,21 @@ dictionary PeerDetails {
227228
boolean is_connected;
228229
};
229230

231+
[Enum]
232+
interface LightningBalance {
233+
ClaimableOnChannelClose ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis );
234+
ClaimableAwaitingConfirmations ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis, u32 confirmation_height );
235+
ContentiousClaimable ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis, u32 timeout_height, PaymentHash payment_hash, PaymentPreimage payment_preimage );
236+
MaybeTimeoutClaimableHTLC ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis, u32 claimable_height, PaymentHash payment_hash);
237+
MaybePreimageClaimableHTLC ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis, u32 expiry_height, PaymentHash payment_hash);
238+
CounterpartyRevokedOutputClaimable ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis );
239+
};
240+
241+
dictionary BalanceDetails {
242+
u64 total_lightning_balance_sats;
243+
sequence<LightningBalance> lightning_balances;
244+
};
245+
230246
interface ChannelConfig {
231247
constructor();
232248
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;
@@ -97,6 +98,7 @@ pub use bitcoin;
9798
pub use lightning;
9899
pub use lightning_invoice;
99100

101+
pub use balance::{BalanceDetails, LightningBalance};
100102
pub use error::Error as NodeError;
101103
use error::Error;
102104

@@ -1554,6 +1556,36 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
15541556
self.payment_store.remove(&payment_hash)
15551557
}
15561558

1559+
/// Retrieves an overview of all known balances.
1560+
pub fn list_balances(&self) -> BalanceDetails {
1561+
let mut total_lightning_balance_sats = 0;
1562+
let mut lightning_balances = Vec::new();
1563+
for funding_txo in self.chain_monitor.list_monitors() {
1564+
match self.chain_monitor.get_monitor(funding_txo) {
1565+
Ok(monitor) => {
1566+
// TODO: Switch to `channel_id` with LDK 0.0.122: let channel_id = monitor.channel_id();
1567+
let channel_id = funding_txo.to_channel_id();
1568+
// unwrap safety: `get_counterparty_node_id` will always be `Some` after 0.0.110 and
1569+
// LDK Node 0.1 depended on 0.0.115 already.
1570+
let counterparty_node_id = monitor.get_counterparty_node_id().unwrap();
1571+
for ldk_balance in monitor.get_claimable_balances() {
1572+
total_lightning_balance_sats += ldk_balance.claimable_amount_satoshis();
1573+
lightning_balances.push(LightningBalance::from_ldk_balance(
1574+
channel_id,
1575+
counterparty_node_id,
1576+
ldk_balance,
1577+
));
1578+
}
1579+
}
1580+
Err(()) => {
1581+
continue;
1582+
}
1583+
}
1584+
}
1585+
1586+
BalanceDetails { total_lightning_balance_sats, lightning_balances }
1587+
}
1588+
15571589
/// Retrieves all payments that match the given predicate.
15581590
///
15591591
/// For example, you could retrieve all stored outbound payments as follows:

0 commit comments

Comments
 (0)