Skip to content

Commit 4b1c34b

Browse files
committed
Expose sweeper balances via BalanceDetails
1 parent da76465 commit 4b1c34b

File tree

5 files changed

+157
-12
lines changed

5 files changed

+157
-12
lines changed

bindings/ldk_node.udl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,11 +236,19 @@ interface LightningBalance {
236236
CounterpartyRevokedOutputClaimable ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis );
237237
};
238238

239+
[Enum]
240+
interface SweeperBalance {
241+
PendingBroadcast ( ChannelId? channel_id, u64 amount_satoshis );
242+
BroadcastAwaitingConfirmation ( ChannelId? channel_id, u32 latest_broadcast_height, Txid latest_spending_txid, u64 amount_satoshis );
243+
AwaitingThresholdConfirmations ( ChannelId? channel_id, Txid latest_spending_txid, BlockHash confirmation_hash, u32 confirmation_height, u64 amount_satoshis);
244+
};
245+
239246
dictionary BalanceDetails {
240247
u64 total_onchain_balance_sats;
241248
u64 spendable_onchain_balance_sats;
242249
u64 total_lightning_balance_sats;
243250
sequence<LightningBalance> lightning_balances;
251+
sequence<SweeperBalance> sweeper_balances;
244252
};
245253

246254
interface ChannelConfig {
@@ -271,6 +279,9 @@ enum LogLevel {
271279
[Custom]
272280
typedef string Txid;
273281

282+
[Custom]
283+
typedef string BlockHash;
284+
274285
[Custom]
275286
typedef string SocketAddress;
276287

src/balance.rs

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
use bitcoin::secp256k1::PublicKey;
21
use lightning::chain::channelmonitor::Balance as LdkBalance;
32
use lightning::ln::{ChannelId, PaymentHash, PaymentPreimage};
43

4+
use bitcoin::secp256k1::PublicKey;
5+
use bitcoin::{BlockHash, Txid};
6+
7+
use crate::sweep::SpendableOutputInfo;
8+
59
/// Details of the known available balances returned by [`Node::list_balances`].
610
///
711
/// [`Node::list_balances`]: crate::Node::list_balances
@@ -24,6 +28,15 @@ pub struct BalanceDetails {
2428
pub total_lightning_balance_sats: u64,
2529
/// A detailed list of all known Lightning balances.
2630
pub lightning_balances: Vec<LightningBalance>,
31+
/// A detailed list of balances currently being swept from the Lightning to the on-chain
32+
/// wallet.
33+
///
34+
/// These are balances resulting from channel closures that may have been encumbered by a
35+
/// delay, but are now being claimed and useable once sufficiently confirmed on-chain.
36+
///
37+
/// Note that, depending on the sync status of the wallets, swept balances listed here might or
38+
/// might not already be accounted for in [`Self::total_onchain_balance_sats`].
39+
pub sweeper_balances: Vec<SweeperBalance>,
2740
}
2841

2942
/// Details about the status of a known Lightning balance.
@@ -190,3 +203,90 @@ impl LightningBalance {
190203
}
191204
}
192205
}
206+
207+
/// Details about the status of a known balance currently being swept to our on-chain wallet.
208+
#[derive(Debug, Clone)]
209+
pub enum SweeperBalance {
210+
/// The spendable output is about to be swept, but a spending transaction has yet to be generated and
211+
/// broadcast.
212+
PendingBroadcast {
213+
/// The identifier of the channel this balance belongs to.
214+
channel_id: Option<ChannelId>,
215+
/// The amount, in satoshis, of the output being swept.
216+
amount_satoshis: u64,
217+
},
218+
/// A spending transaction has been generated and broadcast and is awaiting confirmation
219+
/// on-chain.
220+
BroadcastAwaitingConfirmation {
221+
/// The identifier of the channel this balance belongs to.
222+
channel_id: Option<ChannelId>,
223+
/// The best height when we last broadcast a transaction spending the output being swept.
224+
latest_broadcast_height: u32,
225+
/// The identifier of the transaction spending the swept output we last broadcast.
226+
latest_spending_txid: Txid,
227+
/// The amount, in satoshis, of the output being swept.
228+
amount_satoshis: u64,
229+
},
230+
/// A spending transaction has been confirmed on-chain and is awaiting threshold confirmations.
231+
///
232+
/// It will be considered irrevocably confirmed after reaching [`ANTI_REORG_DELAY`].
233+
///
234+
/// [`ANTI_REORG_DELAY`]: lightning::chain::channelmonitor::ANTI_REORG_DELAY
235+
AwaitingThresholdConfirmations {
236+
/// The identifier of the channel this balance belongs to.
237+
channel_id: Option<ChannelId>,
238+
/// The identifier of the confirmed transaction spending the swept output.
239+
latest_spending_txid: Txid,
240+
/// The hash of the block in which the spending transaction was confirmed.
241+
confirmation_hash: BlockHash,
242+
/// The height at which the spending transaction was confirmed.
243+
confirmation_height: u32,
244+
/// The amount, in satoshis, of the output being swept.
245+
amount_satoshis: u64,
246+
},
247+
}
248+
249+
impl SweeperBalance {
250+
pub(crate) fn from_tracked_spendable_output(output_info: SpendableOutputInfo) -> Self {
251+
if let Some(confirmation_hash) = output_info.confirmation_hash {
252+
debug_assert!(output_info.confirmation_height.is_some());
253+
debug_assert!(output_info.latest_spending_tx.is_some());
254+
let channel_id = output_info.channel_id;
255+
let confirmation_height = output_info
256+
.confirmation_height
257+
.expect("Height must be set if the output is confirmed");
258+
let latest_spending_txid = output_info
259+
.latest_spending_tx
260+
.as_ref()
261+
.expect("Spending tx must be set if the output is confirmed")
262+
.txid();
263+
let amount_satoshis = output_info.value_satoshis();
264+
Self::AwaitingThresholdConfirmations {
265+
channel_id,
266+
latest_spending_txid,
267+
confirmation_hash,
268+
confirmation_height,
269+
amount_satoshis,
270+
}
271+
} else if let Some(latest_broadcast_height) = output_info.latest_broadcast_height {
272+
debug_assert!(output_info.latest_spending_tx.is_some());
273+
let channel_id = output_info.channel_id;
274+
let latest_spending_txid = output_info
275+
.latest_spending_tx
276+
.as_ref()
277+
.expect("Spending tx must be set if the spend was broadcast")
278+
.txid();
279+
let amount_satoshis = output_info.value_satoshis();
280+
Self::BroadcastAwaitingConfirmation {
281+
channel_id,
282+
latest_broadcast_height,
283+
latest_spending_txid,
284+
amount_satoshis,
285+
}
286+
} else {
287+
let channel_id = output_info.channel_id;
288+
let amount_satoshis = output_info.value_satoshis();
289+
Self::PendingBroadcast { channel_id, amount_satoshis }
290+
}
291+
}
292+
}

src/lib.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ pub use bitcoin;
9898
pub use lightning;
9999
pub use lightning_invoice;
100100

101-
pub use balance::{BalanceDetails, LightningBalance};
101+
pub use balance::{BalanceDetails, LightningBalance, SweeperBalance};
102102
pub use error::Error as NodeError;
103103
use error::Error;
104104

@@ -108,7 +108,10 @@ pub use types::ChannelConfig;
108108
pub use io::utils::generate_entropy_mnemonic;
109109

110110
#[cfg(feature = "uniffi")]
111-
use {bip39::Mnemonic, bitcoin::OutPoint, lightning::ln::PaymentSecret, uniffi_types::*};
111+
use {
112+
bip39::Mnemonic, bitcoin::BlockHash, bitcoin::OutPoint, lightning::ln::PaymentSecret,
113+
uniffi_types::*,
114+
};
112115

113116
#[cfg(feature = "uniffi")]
114117
pub use builder::ArcedNodeBuilder as Builder;
@@ -1588,11 +1591,19 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
15881591
}
15891592
}
15901593

1594+
let sweeper_balances = self
1595+
.output_sweeper
1596+
.tracked_spendable_outputs()
1597+
.into_iter()
1598+
.map(|o| SweeperBalance::from_tracked_spendable_output(o))
1599+
.collect();
1600+
15911601
BalanceDetails {
15921602
total_onchain_balance_sats,
15931603
spendable_onchain_balance_sats,
15941604
total_lightning_balance_sats,
15951605
lightning_balances,
1606+
sweeper_balances,
15961607
}
15971608
}
15981609

src/sweep.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ const REGENERATE_SPEND_THRESHOLD: u32 = 144;
2929

3030
#[derive(Clone, Debug, PartialEq, Eq)]
3131
pub(crate) struct SpendableOutputInfo {
32-
id: [u8; 32],
33-
descriptor: SpendableOutputDescriptor,
34-
channel_id: Option<ChannelId>,
35-
first_broadcast_hash: Option<BlockHash>,
36-
latest_broadcast_height: Option<u32>,
37-
latest_spending_tx: Option<Transaction>,
38-
confirmation_height: Option<u32>,
39-
confirmation_hash: Option<BlockHash>,
32+
pub(crate) id: [u8; 32],
33+
pub(crate) descriptor: SpendableOutputDescriptor,
34+
pub(crate) channel_id: Option<ChannelId>,
35+
pub(crate) first_broadcast_hash: Option<BlockHash>,
36+
pub(crate) latest_broadcast_height: Option<u32>,
37+
pub(crate) latest_spending_tx: Option<Transaction>,
38+
pub(crate) confirmation_height: Option<u32>,
39+
pub(crate) confirmation_hash: Option<BlockHash>,
4040
}
4141

4242
impl SpendableOutputInfo {
@@ -77,6 +77,14 @@ impl SpendableOutputInfo {
7777

7878
false
7979
}
80+
81+
pub(crate) fn value_satoshis(&self) -> u64 {
82+
match &self.descriptor {
83+
SpendableOutputDescriptor::StaticOutput { output, .. } => output.value,
84+
SpendableOutputDescriptor::DelayedPaymentOutput(output) => output.output.value,
85+
SpendableOutputDescriptor::StaticPaymentOutput(output) => output.output.value,
86+
}
87+
}
8088
}
8189

8290
impl_writeable_tlv_based!(SpendableOutputInfo, {
@@ -184,6 +192,10 @@ where
184192
self.rebroadcast_if_necessary();
185193
}
186194

195+
pub(crate) fn tracked_spendable_outputs(&self) -> Vec<SpendableOutputInfo> {
196+
self.outputs.lock().unwrap().clone()
197+
}
198+
187199
fn rebroadcast_if_necessary(&self) {
188200
let (cur_height, cur_hash) = {
189201
let best_block = self.best_block.lock().unwrap();

src/uniffi_types.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{Node, SocketAddress, UserChannelId};
88
use bitcoin::hashes::sha256::Hash as Sha256;
99
use bitcoin::hashes::Hash;
1010
use bitcoin::secp256k1::PublicKey;
11-
use bitcoin::{Address, Txid};
11+
use bitcoin::{Address, BlockHash, Txid};
1212
use lightning::ln::{ChannelId, PaymentHash, PaymentPreimage, PaymentSecret};
1313
use lightning_invoice::{Bolt11Invoice, SignedRawBolt11Invoice};
1414

@@ -165,6 +165,17 @@ impl UniffiCustomTypeConverter for Txid {
165165
}
166166
}
167167

168+
impl UniffiCustomTypeConverter for BlockHash {
169+
type Builtin = String;
170+
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
171+
Ok(BlockHash::from_str(&val)?)
172+
}
173+
174+
fn from_custom(obj: Self) -> Self::Builtin {
175+
obj.to_string()
176+
}
177+
}
178+
168179
impl UniffiCustomTypeConverter for Mnemonic {
169180
type Builtin = String;
170181
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {

0 commit comments

Comments
 (0)