Skip to content

Commit 97bbbe0

Browse files
committed
Expose sweeper balances via BalanceDetails
1 parent ddf5e4a commit 97bbbe0

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
@@ -250,11 +250,19 @@ interface LightningBalance {
250250
CounterpartyRevokedOutputClaimable ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis );
251251
};
252252

253+
[Enum]
254+
interface SweeperBalance {
255+
PendingBroadcast ( ChannelId? channel_id, u64 amount_satoshis );
256+
BroadcastAwaitingConfirmation ( ChannelId? channel_id, u32 latest_broadcast_height, Txid latest_spending_txid, u64 amount_satoshis );
257+
AwaitingThresholdConfirmations ( ChannelId? channel_id, Txid latest_spending_txid, BlockHash confirmation_hash, u32 confirmation_height, u64 amount_satoshis);
258+
};
259+
253260
dictionary BalanceDetails {
254261
u64 total_onchain_balance_sats;
255262
u64 spendable_onchain_balance_sats;
256263
u64 total_lightning_balance_sats;
257264
sequence<LightningBalance> lightning_balances;
265+
sequence<SweeperBalance> sweeper_balances;
258266
};
259267

260268
interface ChannelConfig {
@@ -285,6 +293,9 @@ enum LogLevel {
285293
[Custom]
286294
typedef string Txid;
287295

296+
[Custom]
297+
typedef string BlockHash;
298+
288299
[Custom]
289300
typedef string SocketAddress;
290301

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
@@ -33,6 +37,15 @@ pub struct BalanceDetails {
3337
/// [`ChannelDetails::next_outbound_htlc_limit_msat`]: crate::ChannelDetails::next_outbound_htlc_limit_msat
3438
/// [`Node::list_channels`]: crate::Node::list_channels
3539
pub lightning_balances: Vec<LightningBalance>,
40+
/// A detailed list of balances currently being swept from the Lightning to the on-chain
41+
/// wallet.
42+
///
43+
/// These are balances resulting from channel closures that may have been encumbered by a
44+
/// delay, but are now being claimed and useable once sufficiently confirmed on-chain.
45+
///
46+
/// Note that, depending on the sync status of the wallets, swept balances listed here might or
47+
/// might not already be accounted for in [`Self::total_onchain_balance_sats`].
48+
pub sweeper_balances: Vec<SweeperBalance>,
3649
}
3750

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

src/lib.rs

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

103-
pub use balance::{BalanceDetails, LightningBalance};
103+
pub use balance::{BalanceDetails, LightningBalance, SweeperBalance};
104104
pub use error::Error as NodeError;
105105
use error::Error;
106106

@@ -110,7 +110,10 @@ pub use types::ChannelConfig;
110110
pub use io::utils::generate_entropy_mnemonic;
111111

112112
#[cfg(feature = "uniffi")]
113-
use {bip39::Mnemonic, bitcoin::OutPoint, lightning::ln::PaymentSecret, uniffi_types::*};
113+
use {
114+
bip39::Mnemonic, bitcoin::BlockHash, bitcoin::OutPoint, lightning::ln::PaymentSecret,
115+
uniffi_types::*,
116+
};
114117

115118
#[cfg(feature = "uniffi")]
116119
pub use builder::ArcedNodeBuilder as Builder;
@@ -1771,11 +1774,19 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
17711774
}
17721775
}
17731776

1777+
let sweeper_balances = self
1778+
.output_sweeper
1779+
.tracked_spendable_outputs()
1780+
.into_iter()
1781+
.map(|o| SweeperBalance::from_tracked_spendable_output(o))
1782+
.collect();
1783+
17741784
BalanceDetails {
17751785
total_onchain_balance_sats,
17761786
spendable_onchain_balance_sats,
17771787
total_lightning_balance_sats,
17781788
lightning_balances,
1789+
sweeper_balances,
17791790
}
17801791
}
17811792

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)