Skip to content

Commit

Permalink
Expose sweeper balances via BalanceDetails
Browse files Browse the repository at this point in the history
  • Loading branch information
tnull committed Feb 16, 2024
1 parent 8495bc7 commit 17a6c9f
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 12 deletions.
11 changes: 11 additions & 0 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,19 @@ interface LightningBalance {
CounterpartyRevokedOutputClaimable ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis );
};

[Enum]
interface SweeperBalance {
PendingBroadcast ( ChannelId? channel_id, u64 amount_satoshis );
BroadcastAwaitingConfirmation ( ChannelId? channel_id, u32 latest_broadcast_height, Txid latest_spending_txid, u64 amount_satoshis );
AwaitingThresholdConfirmations ( ChannelId? channel_id, Txid latest_spending_txid, BlockHash confirmation_hash, u32 confirmation_height, u64 amount_satoshis);
};

dictionary BalanceDetails {
u64 total_onchain_balance_sats;
u64 spendable_onchain_balance_sats;
u64 total_lightning_balance_sats;
sequence<LightningBalance> lightning_balances;
sequence<SweeperBalance> sweeper_balances;
};

interface ChannelConfig {
Expand Down Expand Up @@ -269,6 +277,9 @@ enum LogLevel {
[Custom]
typedef string Txid;

[Custom]
typedef string BlockHash;

[Custom]
typedef string SocketAddress;

Expand Down
102 changes: 101 additions & 1 deletion src/balance.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use bitcoin::secp256k1::PublicKey;
use lightning::chain::channelmonitor::Balance as LdkBalance;
use lightning::ln::{ChannelId, PaymentHash, PaymentPreimage};

use bitcoin::secp256k1::PublicKey;
use bitcoin::{BlockHash, Txid};

use crate::sweep::SpendableOutputInfo;

/// Details of the known available balances returned by [`Node::list_balances`].
///
/// [`Node::list_balances`]: crate::Node::list_balances
Expand All @@ -15,6 +19,15 @@ pub struct BalanceDetails {
pub total_lightning_balance_sats: u64,
/// A detailed list of all known Lightning balances.
pub lightning_balances: Vec<LightningBalance>,
/// A detailed list of balances currently being swept from the Lightning to the on-chain
/// wallet.
///
/// These are balances resulting from channel closures that may have been encumbered by a
/// delay, but are now being claimed and useable once sufficiently confirmed on-chain.
///
/// Note that, depending on the sync status of the wallets, swept balances listed here might or
/// might not already be accounted for in [`Self::total_onchain_balance_sats`].
pub sweeper_balances: Vec<SweeperBalance>,
}

/// Details about the status of a known Lightning balance.
Expand Down Expand Up @@ -184,3 +197,90 @@ impl LightningBalance {
}
}
}

/// Details about the status of a known balance currently being swept to our on-chain wallet.
#[derive(Debug, Clone)]
pub enum SweeperBalance {
/// The spendable output is about to be swept, but a spending transaction has yet to be generated and
/// broadcast.
PendingBroadcast {
/// The identifier of the channel this balance belongs to.
channel_id: Option<ChannelId>,
/// The amount, in satoshis, of the output being swept.
amount_satoshis: u64,
},
/// A spending transaction has been generated and broadcast and is awaiting confirmation
/// on-chain.
BroadcastAwaitingConfirmation {
/// The identifier of the channel this balance belongs to.
channel_id: Option<ChannelId>,
/// The best height when we last broadcast a transaction spending the output being swept.
latest_broadcast_height: u32,
/// The identifier of the transaction spending the swept output we last broadcast.
latest_spending_txid: Txid,
/// The amount, in satoshis, of the output being swept.
amount_satoshis: u64,
},
/// A spending transaction has been confirmed on-chain and is awaiting threshold confirmations.
///
/// It will be considered irrevocably confirmed after reaching [`ANTI_REORG_DELAY`].
///
/// [`ANTI_REORG_DELAY`]: lightning::chain::channelmonitor::ANTI_REORG_DELAY
AwaitingThresholdConfirmations {
/// The identifier of the channel this balance belongs to.
channel_id: Option<ChannelId>,
/// The identifier of the confirmed transaction spending the swept output.
latest_spending_txid: Txid,
/// The hash of the block in which the spending transaction was confirmed.
confirmation_hash: BlockHash,
/// The height at which the spending transaction was confirmed.
confirmation_height: u32,
/// The amount, in satoshis, of the output being swept.
amount_satoshis: u64,
},
}

impl SweeperBalance {
pub(crate) fn from_tracked_spendable_output(output_info: SpendableOutputInfo) -> Self {
if let Some(confirmation_hash) = output_info.confirmation_hash {
debug_assert!(output_info.confirmation_height.is_some());
debug_assert!(output_info.latest_spending_tx.is_some());
let channel_id = output_info.channel_id;
let confirmation_height = output_info
.confirmation_height
.expect("Height must be set if the output is confirmed");
let latest_spending_txid = output_info
.latest_spending_tx
.as_ref()
.expect("Spending tx must be set if the output is confirmed")
.txid();
let amount_satoshis = output_info.value_satoshis();
Self::AwaitingThresholdConfirmations {
channel_id,
latest_spending_txid,
confirmation_hash,
confirmation_height,
amount_satoshis,
}
} else if let Some(latest_broadcast_height) = output_info.latest_broadcast_height {
debug_assert!(output_info.latest_spending_tx.is_some());
let channel_id = output_info.channel_id;
let latest_spending_txid = output_info
.latest_spending_tx
.as_ref()
.expect("Spending tx must be set if the spend was broadcast")
.txid();
let amount_satoshis = output_info.value_satoshis();
Self::BroadcastAwaitingConfirmation {
channel_id,
latest_broadcast_height,
latest_spending_txid,
amount_satoshis,
}
} else {
let channel_id = output_info.channel_id;
let amount_satoshis = output_info.value_satoshis();
Self::PendingBroadcast { channel_id, amount_satoshis }
}
}
}
15 changes: 13 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ pub use bitcoin;
pub use lightning;
pub use lightning_invoice;

pub use balance::{BalanceDetails, LightningBalance};
pub use balance::{BalanceDetails, LightningBalance, SweeperBalance};
pub use error::Error as NodeError;
use error::Error;

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

#[cfg(feature = "uniffi")]
use {bip39::Mnemonic, bitcoin::OutPoint, lightning::ln::PaymentSecret, uniffi_types::*};
use {
bip39::Mnemonic, bitcoin::BlockHash, bitcoin::OutPoint, lightning::ln::PaymentSecret,
uniffi_types::*,
};

#[cfg(feature = "uniffi")]
pub use builder::ArcedNodeBuilder as Builder;
Expand Down Expand Up @@ -1579,11 +1582,19 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
}
}

let sweeper_balances = self
.output_sweeper
.tracked_spendable_outputs()
.into_iter()
.map(|o| SweeperBalance::from_tracked_spendable_output(o))
.collect();

BalanceDetails {
total_onchain_balance_sats,
spendable_onchain_balance_sats,
total_lightning_balance_sats,
lightning_balances,
sweeper_balances,
}
}

Expand Down
28 changes: 20 additions & 8 deletions src/sweep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ const REGENERATE_SPEND_THRESHOLD: u32 = 144;

#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct SpendableOutputInfo {
id: [u8; 32],
descriptor: SpendableOutputDescriptor,
channel_id: Option<ChannelId>,
first_broadcast_hash: Option<BlockHash>,
latest_broadcast_height: Option<u32>,
latest_spending_tx: Option<Transaction>,
confirmation_height: Option<u32>,
confirmation_hash: Option<BlockHash>,
pub(crate) id: [u8; 32],
pub(crate) descriptor: SpendableOutputDescriptor,
pub(crate) channel_id: Option<ChannelId>,
pub(crate) first_broadcast_hash: Option<BlockHash>,
pub(crate) latest_broadcast_height: Option<u32>,
pub(crate) latest_spending_tx: Option<Transaction>,
pub(crate) confirmation_height: Option<u32>,
pub(crate) confirmation_hash: Option<BlockHash>,
}

impl SpendableOutputInfo {
Expand Down Expand Up @@ -77,6 +77,14 @@ impl SpendableOutputInfo {

false
}

pub(crate) fn value_satoshis(&self) -> u64 {
match &self.descriptor {
SpendableOutputDescriptor::StaticOutput { output, .. } => output.value,
SpendableOutputDescriptor::DelayedPaymentOutput(output) => output.output.value,
SpendableOutputDescriptor::StaticPaymentOutput(output) => output.output.value,
}
}
}

impl_writeable_tlv_based!(SpendableOutputInfo, {
Expand Down Expand Up @@ -184,6 +192,10 @@ where
self.rebroadcast_if_necessary();
}

pub(crate) fn tracked_spendable_outputs(&self) -> Vec<SpendableOutputInfo> {
self.outputs.lock().unwrap().clone()
}

fn rebroadcast_if_necessary(&self) {
let (cur_height, cur_hash) = {
let best_block = self.best_block.lock().unwrap();
Expand Down
13 changes: 12 additions & 1 deletion src/uniffi_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{Node, SocketAddress, UserChannelId};
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::Hash;
use bitcoin::secp256k1::PublicKey;
use bitcoin::{Address, Txid};
use bitcoin::{Address, BlockHash, Txid};
use lightning::ln::{ChannelId, PaymentHash, PaymentPreimage, PaymentSecret};
use lightning_invoice::{Bolt11Invoice, SignedRawBolt11Invoice};

Expand Down Expand Up @@ -165,6 +165,17 @@ impl UniffiCustomTypeConverter for Txid {
}
}

impl UniffiCustomTypeConverter for BlockHash {
type Builtin = String;
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
Ok(BlockHash::from_str(&val)?)
}

fn from_custom(obj: Self) -> Self::Builtin {
obj.to_string()
}
}

impl UniffiCustomTypeConverter for Mnemonic {
type Builtin = String;
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
Expand Down

0 comments on commit 17a6c9f

Please sign in to comment.