ldk_node/
balance.rs

1// This file is Copyright its original authors, visible in version control history.
2//
3// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
6// accordance with one or both of these licenses.
7
8use bitcoin::secp256k1::PublicKey;
9use bitcoin::{Amount, BlockHash, Txid};
10use lightning::chain::channelmonitor::{Balance as LdkBalance, BalanceSource};
11use lightning::ln::types::ChannelId;
12use lightning::sign::SpendableOutputDescriptor;
13use lightning::util::sweep::{OutputSpendStatus, TrackedSpendableOutput};
14use lightning_types::payment::{PaymentHash, PaymentPreimage};
15
16/// Details of the known available balances returned by [`Node::list_balances`].
17///
18/// [`Node::list_balances`]: crate::Node::list_balances
19#[derive(Debug, Clone)]
20pub struct BalanceDetails {
21	/// The total balance of our on-chain wallet.
22	pub total_onchain_balance_sats: u64,
23	/// The currently spendable balance of our on-chain wallet.
24	///
25	/// This includes any sufficiently confirmed funds, minus
26	/// [`total_anchor_channels_reserve_sats`].
27	///
28	/// [`total_anchor_channels_reserve_sats`]: Self::total_anchor_channels_reserve_sats
29	pub spendable_onchain_balance_sats: u64,
30	/// The share of our total balance that we retain as an emergency reserve to (hopefully) be
31	/// able to spend the Anchor outputs when one of our channels is closed.
32	pub total_anchor_channels_reserve_sats: u64,
33	/// The total balance that we would be able to claim across all our Lightning channels.
34	///
35	/// Note this excludes balances that we are unsure if we are able to claim (e.g., as we are
36	/// waiting for a preimage or for a timeout to expire). These balances will however be included
37	/// as [`MaybePreimageClaimableHTLC`] and
38	/// [`MaybeTimeoutClaimableHTLC`] in [`lightning_balances`].
39	///
40	/// [`MaybePreimageClaimableHTLC`]: LightningBalance::MaybePreimageClaimableHTLC
41	/// [`MaybeTimeoutClaimableHTLC`]: LightningBalance::MaybeTimeoutClaimableHTLC
42	/// [`lightning_balances`]: Self::lightning_balances
43	pub total_lightning_balance_sats: u64,
44	/// A detailed list of all known Lightning balances that would be claimable on channel closure.
45	///
46	/// Note that less than the listed amounts are spendable over lightning as further reserve
47	/// restrictions apply. Please refer to [`ChannelDetails::outbound_capacity_msat`] and
48	/// [`ChannelDetails::next_outbound_htlc_limit_msat`] as returned by [`Node::list_channels`]
49	/// for a better approximation of the spendable amounts.
50	///
51	/// [`ChannelDetails::outbound_capacity_msat`]: crate::ChannelDetails::outbound_capacity_msat
52	/// [`ChannelDetails::next_outbound_htlc_limit_msat`]: crate::ChannelDetails::next_outbound_htlc_limit_msat
53	/// [`Node::list_channels`]: crate::Node::list_channels
54	pub lightning_balances: Vec<LightningBalance>,
55	/// A detailed list of balances currently being swept from the Lightning to the on-chain
56	/// wallet.
57	///
58	/// These are balances resulting from channel closures that may have been encumbered by a
59	/// delay, but are now being claimed and useable once sufficiently confirmed on-chain.
60	///
61	/// Note that, depending on the sync status of the wallets, swept balances listed here might or
62	/// might not already be accounted for in [`total_onchain_balance_sats`].
63	///
64	/// [`total_onchain_balance_sats`]: Self::total_onchain_balance_sats
65	pub pending_balances_from_channel_closures: Vec<PendingSweepBalance>,
66}
67
68/// Details about the status of a known Lightning balance.
69#[derive(Debug, Clone)]
70pub enum LightningBalance {
71	/// The channel is not yet closed (or the commitment or closing transaction has not yet
72	/// appeared in a block). The given balance is claimable (less on-chain fees) if the channel is
73	/// force-closed now. Values do not take into account any pending splices and are only based
74	/// on the confirmed state of the channel.
75	ClaimableOnChannelClose {
76		/// The identifier of the channel this balance belongs to.
77		channel_id: ChannelId,
78		/// The identifier of our channel counterparty.
79		counterparty_node_id: PublicKey,
80		/// The amount available to claim, in satoshis, excluding the on-chain fees which will be
81		/// required to do so.
82		amount_satoshis: u64,
83		/// The transaction fee we pay for the closing commitment transaction. This amount is not
84		/// included in the `amount_satoshis` value.
85		///
86		/// Note that if this channel is inbound (and thus our counterparty pays the commitment
87		/// transaction fee) this value will be zero. For channels created prior to LDK Node 0.4
88		/// the channel is always treated as outbound (and thus this value is never zero).
89		transaction_fee_satoshis: u64,
90		/// The amount of millisatoshis which has been burned to fees from HTLCs which are outbound
91		/// from us and are related to a payment which was sent by us. This is the sum of the
92		/// millisatoshis part of all HTLCs which are otherwise represented by
93		/// [`LightningBalance::MaybeTimeoutClaimableHTLC`] with their
94		/// [`LightningBalance::MaybeTimeoutClaimableHTLC::outbound_payment`] flag set, as well as
95		/// any dust HTLCs which would otherwise be represented the same.
96		///
97		/// This amount (rounded up to a whole satoshi value) will not be included in `amount_satoshis`.
98		outbound_payment_htlc_rounded_msat: u64,
99		/// The amount of millisatoshis which has been burned to fees from HTLCs which are outbound
100		/// from us and are related to a forwarded HTLC. This is the sum of the millisatoshis part
101		/// of all HTLCs which are otherwise represented by
102		/// [`LightningBalance::MaybeTimeoutClaimableHTLC`] with their
103		/// [`LightningBalance::MaybeTimeoutClaimableHTLC::outbound_payment`] flag *not* set, as
104		/// well as any dust HTLCs which would otherwise be represented the same.
105		///
106		/// This amount (rounded up to a whole satoshi value) will not be included in `amount_satoshis`.
107		outbound_forwarded_htlc_rounded_msat: u64,
108		/// The amount of millisatoshis which has been burned to fees from HTLCs which are inbound
109		/// to us and for which we know the preimage. This is the sum of the millisatoshis part of
110		/// all HTLCs which would be represented by [`LightningBalance::ContentiousClaimable`] on
111		/// channel close, but whose current value is included in `amount_satoshis`, as well as any
112		/// dust HTLCs which would otherwise be represented the same.
113		///
114		/// This amount (rounded up to a whole satoshi value) will not be included in the counterparty's
115		/// `amount_satoshis`.
116		inbound_claiming_htlc_rounded_msat: u64,
117		/// The amount of millisatoshis which has been burned to fees from HTLCs which are inbound
118		/// to us and for which we do not know the preimage. This is the sum of the millisatoshis
119		/// part of all HTLCs which would be represented by
120		/// [`LightningBalance::MaybePreimageClaimableHTLC`] on channel close, as well as any dust
121		/// HTLCs which would otherwise be represented the same.
122		///
123		/// This amount (rounded up to a whole satoshi value) will not be included in the
124		/// counterparty's `amount_satoshis`.
125		inbound_htlc_rounded_msat: u64,
126	},
127	/// The channel has been closed, and the given balance is ours but awaiting confirmations until
128	/// we consider it spendable.
129	ClaimableAwaitingConfirmations {
130		/// The identifier of the channel this balance belongs to.
131		channel_id: ChannelId,
132		/// The identifier of our channel counterparty.
133		counterparty_node_id: PublicKey,
134		/// The amount available to claim, in satoshis, possibly excluding the on-chain fees which
135		/// were spent in broadcasting the transaction.
136		amount_satoshis: u64,
137		/// The height at which an [`Event::SpendableOutputs`] event will be generated for this
138		/// amount.
139		///
140		/// [`Event::SpendableOutputs`]: lightning::events::Event::SpendableOutputs
141		confirmation_height: u32,
142		/// Whether this balance is a result of cooperative close, a force-close, or an HTLC.
143		source: BalanceSource,
144	},
145	/// The channel has been closed, and the given balance should be ours but awaiting spending
146	/// transaction confirmation. If the spending transaction does not confirm in time, it is
147	/// possible our counterparty can take the funds by broadcasting an HTLC timeout on-chain.
148	///
149	/// Once the spending transaction confirms, before it has reached enough confirmations to be
150	/// considered safe from chain reorganizations, the balance will instead be provided via
151	/// [`LightningBalance::ClaimableAwaitingConfirmations`].
152	ContentiousClaimable {
153		/// The identifier of the channel this balance belongs to.
154		channel_id: ChannelId,
155		/// The identifier of our channel counterparty.
156		counterparty_node_id: PublicKey,
157		/// The amount available to claim, in satoshis, excluding the on-chain fees which will be
158		/// required to do so.
159		amount_satoshis: u64,
160		/// The height at which the counterparty may be able to claim the balance if we have not
161		/// done so.
162		timeout_height: u32,
163		/// The payment hash that locks this HTLC.
164		payment_hash: PaymentHash,
165		/// The preimage that can be used to claim this HTLC.
166		payment_preimage: PaymentPreimage,
167	},
168	/// HTLCs which we sent to our counterparty which are claimable after a timeout (less on-chain
169	/// fees) if the counterparty does not know the preimage for the HTLCs. These are somewhat
170	/// likely to be claimed by our counterparty before we do.
171	MaybeTimeoutClaimableHTLC {
172		/// The identifier of the channel this balance belongs to.
173		channel_id: ChannelId,
174		/// The identifier of our channel counterparty.
175		counterparty_node_id: PublicKey,
176		/// The amount potentially available to claim, in satoshis, excluding the on-chain fees
177		/// which will be required to do so.
178		amount_satoshis: u64,
179		/// The height at which we will be able to claim the balance if our counterparty has not
180		/// done so.
181		claimable_height: u32,
182		/// The payment hash whose preimage our counterparty needs to claim this HTLC.
183		payment_hash: PaymentHash,
184		/// Indicates whether this HTLC represents a payment which was sent outbound from us.
185		outbound_payment: bool,
186	},
187	/// HTLCs which we received from our counterparty which are claimable with a preimage which we
188	/// do not currently have. This will only be claimable if we receive the preimage from the node
189	/// to which we forwarded this HTLC before the timeout.
190	MaybePreimageClaimableHTLC {
191		/// The identifier of the channel this balance belongs to.
192		channel_id: ChannelId,
193		/// The identifier of our channel counterparty.
194		counterparty_node_id: PublicKey,
195		/// The amount potentially available to claim, in satoshis, excluding the on-chain fees
196		/// which will be required to do so.
197		amount_satoshis: u64,
198		/// The height at which our counterparty will be able to claim the balance if we have not
199		/// yet received the preimage and claimed it ourselves.
200		expiry_height: u32,
201		/// The payment hash whose preimage we need to claim this HTLC.
202		payment_hash: PaymentHash,
203	},
204	/// The channel has been closed, and our counterparty broadcasted a revoked commitment
205	/// transaction.
206	///
207	/// Thus, we're able to claim all outputs in the commitment transaction, one of which has the
208	/// following amount.
209	CounterpartyRevokedOutputClaimable {
210		/// The identifier of the channel this balance belongs to.
211		channel_id: ChannelId,
212		/// The identifier of our channel counterparty.
213		counterparty_node_id: PublicKey,
214		/// The amount, in satoshis, of the output which we can claim.
215		amount_satoshis: u64,
216	},
217}
218
219impl LightningBalance {
220	pub(crate) fn from_ldk_balance(
221		channel_id: ChannelId, counterparty_node_id: PublicKey, balance: LdkBalance,
222	) -> Self {
223		match balance {
224			LdkBalance::ClaimableOnChannelClose {
225				balance_candidates,
226				confirmed_balance_candidate_index,
227				outbound_payment_htlc_rounded_msat,
228				outbound_forwarded_htlc_rounded_msat,
229				inbound_claiming_htlc_rounded_msat,
230				inbound_htlc_rounded_msat,
231			} => {
232				// unwrap safety: confirmed_balance_candidate_index is guaranteed to index into balance_candidates
233				let balance = balance_candidates.get(confirmed_balance_candidate_index).unwrap();
234
235				Self::ClaimableOnChannelClose {
236					channel_id,
237					counterparty_node_id,
238					amount_satoshis: balance.amount_satoshis,
239					transaction_fee_satoshis: balance.transaction_fee_satoshis,
240					outbound_payment_htlc_rounded_msat,
241					outbound_forwarded_htlc_rounded_msat,
242					inbound_claiming_htlc_rounded_msat,
243					inbound_htlc_rounded_msat,
244				}
245			},
246			LdkBalance::ClaimableAwaitingConfirmations {
247				amount_satoshis,
248				confirmation_height,
249				source,
250			} => Self::ClaimableAwaitingConfirmations {
251				channel_id,
252				counterparty_node_id,
253				amount_satoshis,
254				confirmation_height,
255				source,
256			},
257			LdkBalance::ContentiousClaimable {
258				amount_satoshis,
259				timeout_height,
260				payment_hash,
261				payment_preimage,
262			} => Self::ContentiousClaimable {
263				channel_id,
264				counterparty_node_id,
265				amount_satoshis,
266				timeout_height,
267				payment_hash,
268				payment_preimage,
269			},
270			LdkBalance::MaybeTimeoutClaimableHTLC {
271				amount_satoshis,
272				claimable_height,
273				payment_hash,
274				outbound_payment,
275			} => Self::MaybeTimeoutClaimableHTLC {
276				channel_id,
277				counterparty_node_id,
278				amount_satoshis,
279				claimable_height,
280				payment_hash,
281				outbound_payment,
282			},
283			LdkBalance::MaybePreimageClaimableHTLC {
284				amount_satoshis,
285				expiry_height,
286				payment_hash,
287			} => Self::MaybePreimageClaimableHTLC {
288				channel_id,
289				counterparty_node_id,
290				amount_satoshis,
291				expiry_height,
292				payment_hash,
293			},
294			LdkBalance::CounterpartyRevokedOutputClaimable { amount_satoshis } => {
295				Self::CounterpartyRevokedOutputClaimable {
296					channel_id,
297					counterparty_node_id,
298					amount_satoshis,
299				}
300			},
301		}
302	}
303}
304
305/// Details about the status of a known balance currently being swept to our on-chain wallet.
306#[derive(Debug, Clone)]
307pub enum PendingSweepBalance {
308	/// The spendable output is about to be swept, but a spending transaction has yet to be generated and
309	/// broadcast.
310	PendingBroadcast {
311		/// The identifier of the channel this balance belongs to.
312		channel_id: Option<ChannelId>,
313		/// The amount, in satoshis, of the output being swept.
314		amount_satoshis: u64,
315	},
316	/// A spending transaction has been generated and broadcast and is awaiting confirmation
317	/// on-chain.
318	BroadcastAwaitingConfirmation {
319		/// The identifier of the channel this balance belongs to.
320		channel_id: Option<ChannelId>,
321		/// The best height when we last broadcast a transaction spending the output being swept.
322		latest_broadcast_height: u32,
323		/// The identifier of the transaction spending the swept output we last broadcast.
324		latest_spending_txid: Txid,
325		/// The amount, in satoshis, of the output being swept.
326		amount_satoshis: u64,
327	},
328	/// A spending transaction has been confirmed on-chain and is awaiting threshold confirmations.
329	///
330	/// It will be pruned after reaching [`PRUNE_DELAY_BLOCKS`] confirmations.
331	///
332	/// [`PRUNE_DELAY_BLOCKS`]: lightning::util::sweep::PRUNE_DELAY_BLOCKS
333	AwaitingThresholdConfirmations {
334		/// The identifier of the channel this balance belongs to.
335		channel_id: Option<ChannelId>,
336		/// The identifier of the confirmed transaction spending the swept output.
337		latest_spending_txid: Txid,
338		/// The hash of the block in which the spending transaction was confirmed.
339		confirmation_hash: BlockHash,
340		/// The height at which the spending transaction was confirmed.
341		confirmation_height: u32,
342		/// The amount, in satoshis, of the output being swept.
343		amount_satoshis: u64,
344	},
345}
346
347impl PendingSweepBalance {
348	pub(crate) fn from_tracked_spendable_output(output_info: TrackedSpendableOutput) -> Self {
349		match output_info.status {
350			OutputSpendStatus::PendingInitialBroadcast { .. } => {
351				let channel_id = output_info.channel_id;
352				let amount_satoshis = value_from_descriptor(&output_info.descriptor).to_sat();
353				Self::PendingBroadcast { channel_id, amount_satoshis }
354			},
355			OutputSpendStatus::PendingFirstConfirmation {
356				latest_broadcast_height,
357				latest_spending_tx,
358				..
359			} => {
360				let channel_id = output_info.channel_id;
361				let amount_satoshis = value_from_descriptor(&output_info.descriptor).to_sat();
362				let latest_spending_txid = latest_spending_tx.compute_txid();
363				Self::BroadcastAwaitingConfirmation {
364					channel_id,
365					latest_broadcast_height,
366					latest_spending_txid,
367					amount_satoshis,
368				}
369			},
370			OutputSpendStatus::PendingThresholdConfirmations {
371				latest_spending_tx,
372				confirmation_height,
373				confirmation_hash,
374				..
375			} => {
376				let channel_id = output_info.channel_id;
377				let amount_satoshis = value_from_descriptor(&output_info.descriptor).to_sat();
378				let latest_spending_txid = latest_spending_tx.compute_txid();
379				Self::AwaitingThresholdConfirmations {
380					channel_id,
381					latest_spending_txid,
382					confirmation_hash,
383					confirmation_height,
384					amount_satoshis,
385				}
386			},
387		}
388	}
389}
390
391fn value_from_descriptor(descriptor: &SpendableOutputDescriptor) -> Amount {
392	match &descriptor {
393		SpendableOutputDescriptor::StaticOutput { output, .. } => output.value,
394		SpendableOutputDescriptor::DelayedPaymentOutput(output) => output.output.value,
395		SpendableOutputDescriptor::StaticPaymentOutput(output) => output.output.value,
396	}
397}