Skip to content

Commit

Permalink
Expose LightningBalance/BalanceDetails newtypes
Browse files Browse the repository at this point in the history
  • Loading branch information
tnull committed Feb 15, 2024
1 parent 21ede58 commit 28d5d6b
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 0 deletions.
16 changes: 16 additions & 0 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ interface LDKNode {
PaymentDetails? payment([ByRef]PaymentHash payment_hash);
[Throws=NodeError]
void remove_payment([ByRef]PaymentHash payment_hash);
BalanceDetails list_balances();
sequence<PaymentDetails> list_payments();
sequence<PeerDetails> list_peers();
sequence<ChannelDetails> list_channels();
Expand Down Expand Up @@ -227,6 +228,21 @@ dictionary PeerDetails {
boolean is_connected;
};

[Enum]
interface LightningBalance {
ClaimableOnChannelClose ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis );
ClaimableAwaitingConfirmations ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis, u32 confirmation_height );
ContentiousClaimable ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis, u32 timeout_height, PaymentHash payment_hash, PaymentPreimage payment_preimage );
MaybeTimeoutClaimableHTLC ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis, u32 claimable_height, PaymentHash payment_hash);
MaybePreimageClaimableHTLC ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis, u32 expiry_height, PaymentHash payment_hash);
CounterpartyRevokedOutputClaimable ( ChannelId channel_id, PublicKey counterparty_node_id, u64 amount_satoshis );
};

dictionary BalanceDetails {
u64 total_lightning_balance_sats;
sequence<LightningBalance> lightning_balances;
};

interface ChannelConfig {
constructor();
u32 forwarding_fee_proportional_millionths();
Expand Down
182 changes: 182 additions & 0 deletions src/balance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
use bitcoin::secp256k1::PublicKey;
use lightning::chain::channelmonitor::Balance as LdkBalance;
use lightning::ln::{ChannelId, PaymentHash, PaymentPreimage};

/// Details of the known available balances returned by [`Node::list_balances`].
///
/// [`Node::list_balances`]: crate::Node::list_balances
#[derive(Debug, Clone)]
pub struct BalanceDetails {
/// The total balance that we would be able to claim across all our Lightning channels.
pub total_lightning_balance_sats: u64,
/// A detailed list of all known balances.
pub lightning_balances: Vec<LightningBalance>,
}

/// Details about the status of a known balance.
#[derive(Debug, Clone)]
pub enum LightningBalance {
/// The channel is not yet closed (or the commitment or closing transaction has not yet
/// appeared in a block). The given balance is claimable (less on-chain fees) if the channel is
/// force-closed now.
ClaimableOnChannelClose {
/// The identifier of the channel this balance belongs to.
channel_id: ChannelId,
/// The identfiier of our channel counterparty.
counterparty_node_id: PublicKey,
/// The amount available to claim, in satoshis, excluding the on-chain fees which will be
/// required to do so.
amount_satoshis: u64,
},
/// The channel has been closed, and the given balance is ours but awaiting confirmations until
/// we consider it spendable.
ClaimableAwaitingConfirmations {
/// The identifier of the channel this balance belongs to.
channel_id: ChannelId,
/// The identfiier of our channel counterparty.
counterparty_node_id: PublicKey,
/// The amount available to claim, in satoshis, possibly excluding the on-chain fees which
/// were spent in broadcasting the transaction.
amount_satoshis: u64,
/// The height at which an [`Event::SpendableOutputs`] event will be generated for this
/// amount.
///
/// [`Event::SpendableOutputs`]: lightning::events::Event::SpendableOutputs
confirmation_height: u32,
},
/// The channel has been closed, and the given balance should be ours but awaiting spending
/// transaction confirmation. If the spending transaction does not confirm in time, it is
/// possible our counterparty can take the funds by broadcasting an HTLC timeout on-chain.
///
/// Once the spending transaction confirms, before it has reached enough confirmations to be
/// considered safe from chain reorganizations, the balance will instead be provided via
/// [`LightningBalance::ClaimableAwaitingConfirmations`].
ContentiousClaimable {
/// The identifier of the channel this balance belongs to.
channel_id: ChannelId,
/// The identfiier of our channel counterparty.
counterparty_node_id: PublicKey,
/// The amount available to claim, in satoshis, excluding the on-chain fees which will be
/// required to do so.
amount_satoshis: u64,
/// The height at which the counterparty may be able to claim the balance if we have not
/// done so.
timeout_height: u32,
/// The payment hash that locks this HTLC.
payment_hash: PaymentHash,
/// The preimage that can be used to claim this HTLC.
payment_preimage: PaymentPreimage,
},
/// HTLCs which we sent to our counterparty which are claimable after a timeout (less on-chain
/// fees) if the counterparty does not know the preimage for the HTLCs. These are somewhat
/// likely to be claimed by our counterparty before we do.
MaybeTimeoutClaimableHTLC {
/// The identifier of the channel this balance belongs to.
channel_id: ChannelId,
/// The identfiier of our channel counterparty.
counterparty_node_id: PublicKey,
/// The amount potentially available to claim, in satoshis, excluding the on-chain fees
/// which will be required to do so.
amount_satoshis: u64,
/// The height at which we will be able to claim the balance if our counterparty has not
/// done so.
claimable_height: u32,
/// The payment hash whose preimage our counterparty needs to claim this HTLC.
payment_hash: PaymentHash,
},
/// HTLCs which we received from our counterparty which are claimable with a preimage which we
/// do not currently have. This will only be claimable if we receive the preimage from the node
/// to which we forwarded this HTLC before the timeout.
MaybePreimageClaimableHTLC {
/// The identifier of the channel this balance belongs to.
channel_id: ChannelId,
/// The identfiier of our channel counterparty.
counterparty_node_id: PublicKey,
/// The amount potentially available to claim, in satoshis, excluding the on-chain fees
/// which will be required to do so.
amount_satoshis: u64,
/// The height at which our counterparty will be able to claim the balance if we have not
/// yet received the preimage and claimed it ourselves.
expiry_height: u32,
/// The payment hash whose preimage we need to claim this HTLC.
payment_hash: PaymentHash,
},
/// The channel has been closed, and our counterparty broadcasted a revoked commitment
/// transaction.
///
/// Thus, we're able to claim all outputs in the commitment transaction, one of which has the
/// following amount.
CounterpartyRevokedOutputClaimable {
/// The identifier of the channel this balance belongs to.
channel_id: ChannelId,
/// The identfiier of our channel counterparty.
counterparty_node_id: PublicKey,
/// The amount, in satoshis, of the output which we can claim.
///
/// Note that for outputs from HTLC balances this may be excluding some on-chain fees that
/// were already spent.
amount_satoshis: u64,
},
}

impl LightningBalance {
pub(crate) fn from_ldk_balance(
channel_id: ChannelId, counterparty_node_id: PublicKey, balance: LdkBalance,
) -> Self {
match balance {
LdkBalance::ClaimableOnChannelClose { amount_satoshis } => {
Self::ClaimableOnChannelClose { channel_id, counterparty_node_id, amount_satoshis }
}
LdkBalance::ClaimableAwaitingConfirmations { amount_satoshis, confirmation_height } => {
Self::ClaimableAwaitingConfirmations {
channel_id,
counterparty_node_id,
amount_satoshis,
confirmation_height,
}
}
LdkBalance::ContentiousClaimable {
amount_satoshis,
timeout_height,
payment_hash,
payment_preimage,
} => Self::ContentiousClaimable {
channel_id,
counterparty_node_id,
amount_satoshis,
timeout_height,
payment_hash,
payment_preimage,
},
LdkBalance::MaybeTimeoutClaimableHTLC {
amount_satoshis,
claimable_height,
payment_hash,
} => Self::MaybeTimeoutClaimableHTLC {
channel_id,
counterparty_node_id,
amount_satoshis,
claimable_height,
payment_hash,
},
LdkBalance::MaybePreimageClaimableHTLC {
amount_satoshis,
expiry_height,
payment_hash,
} => Self::MaybePreimageClaimableHTLC {
channel_id,
counterparty_node_id,
amount_satoshis,
expiry_height,
payment_hash,
},
LdkBalance::CounterpartyRevokedOutputClaimable { amount_satoshis } => {
Self::CounterpartyRevokedOutputClaimable {
channel_id,
counterparty_node_id,
amount_satoshis,
}
}
}
}
}
32 changes: 32 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
#![allow(ellipsis_inclusive_range_patterns)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]

mod balance;
mod builder;
mod error;
mod event;
Expand All @@ -97,6 +98,7 @@ pub use bitcoin;
pub use lightning;
pub use lightning_invoice;

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

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

/// Retrieves an overview of all known balances.
pub fn list_balances(&self) -> BalanceDetails {
let mut total_lightning_balance_sats = 0;
let mut lightning_balances = Vec::new();
for funding_txo in self.chain_monitor.list_monitors() {
match self.chain_monitor.get_monitor(funding_txo) {
Ok(monitor) => {
// TODO: Switch to `channel_id` with LDK 0.0.122: let channel_id = monitor.channel_id();
let channel_id = funding_txo.to_channel_id();
// unwrap safety: `get_counterparty_node_id` will always be `Some` after 0.0.110 and
// LDK Node 0.1 depended on 0.0.115 already.
let counterparty_node_id = monitor.get_counterparty_node_id().unwrap();
for ldk_balance in monitor.get_claimable_balances() {
total_lightning_balance_sats += ldk_balance.claimable_amount_satoshis();
lightning_balances.push(LightningBalance::from_ldk_balance(
channel_id,
counterparty_node_id,
ldk_balance,
));
}
}
Err(()) => {
continue;
}
}
}

BalanceDetails { total_lightning_balance_sats, lightning_balances }
}

/// Retrieves all payments that match the given predicate.
///
/// For example, you could retrieve all stored outbound payments as follows:
Expand Down

0 comments on commit 28d5d6b

Please sign in to comment.