Skip to content

Commit e5594ea

Browse files
authored
Merge pull request #4029 from wpaulino/splice-claimable-balances
Account for splices in claimable balances
2 parents 560150d + 5adf873 commit e5594ea

File tree

2 files changed

+150
-53
lines changed

2 files changed

+150
-53
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 97 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,24 @@ pub enum BalanceSource {
777777
Htlc,
778778
}
779779

780+
/// The claimable balance of a holder commitment transaction that has yet to be broadcast.
781+
#[derive(Clone, Debug, PartialEq, Eq)]
782+
#[cfg_attr(test, derive(PartialOrd, Ord))]
783+
pub struct HolderCommitmentTransactionBalance {
784+
/// The amount available to claim, in satoshis, excluding the on-chain fees which will be
785+
/// required to do so.
786+
pub amount_satoshis: u64,
787+
/// The transaction fee we pay for the closing commitment transaction. This amount is not
788+
/// included in the [`HolderCommitmentTransactionBalance::amount_satoshis`] value.
789+
/// This amount includes the sum of dust HTLCs on the commitment transaction, any elided anchors,
790+
/// as well as the sum of msat amounts rounded down from non-dust HTLCs.
791+
///
792+
/// Note that if this channel is inbound (and thus our counterparty pays the commitment
793+
/// transaction fee) this value will be zero. For [`ChannelMonitor`]s created prior to LDK
794+
/// 0.0.124, the channel is always treated as outbound (and thus this value is never zero).
795+
pub transaction_fee_satoshis: u64,
796+
}
797+
780798
/// Details about the balance(s) available for spending once the channel appears on chain.
781799
///
782800
/// See [`ChannelMonitor::get_claimable_balances`] for more details on when these will or will not
@@ -785,21 +803,26 @@ pub enum BalanceSource {
785803
#[cfg_attr(test, derive(PartialOrd, Ord))]
786804
pub enum Balance {
787805
/// The channel is not yet closed (or the commitment or closing transaction has not yet
788-
/// appeared in a block). The given balance is claimable (less on-chain fees) if the channel is
789-
/// force-closed now.
806+
/// appeared in a block).
790807
ClaimableOnChannelClose {
791-
/// The amount available to claim, in satoshis, excluding the on-chain fees which will be
792-
/// required to do so.
793-
amount_satoshis: u64,
794-
/// The transaction fee we pay for the closing commitment transaction. This amount is not
795-
/// included in the [`Balance::ClaimableOnChannelClose::amount_satoshis`] value.
796-
/// This amount includes the sum of dust HTLCs on the commitment transaction, any elided anchors,
797-
/// as well as the sum of msat amounts rounded down from non-dust HTLCs.
808+
/// A list of balance candidates based on the latest set of valid holder commitment
809+
/// transactions that can hit the chain. Typically, a channel only has one valid holder
810+
/// commitment transaction that spends the current funding output. As soon as a channel is
811+
/// spliced, an alternative holder commitment transaction exists spending the new funding
812+
/// output. More alternative holder commitment transactions can exist as the splice remains
813+
/// pending and RBF attempts are made.
798814
///
799-
/// Note that if this channel is inbound (and thus our counterparty pays the commitment
800-
/// transaction fee) this value will be zero. For [`ChannelMonitor`]s created prior to LDK
801-
/// 0.0.124, the channel is always treated as outbound (and thus this value is never zero).
802-
transaction_fee_satoshis: u64,
815+
/// The candidates are sorted by the order in which the holder commitment transactions were
816+
/// negotiated. When only one candidate exists, the channel does not have a splice pending.
817+
/// When multiple candidates exist, the last one reflects the balance of the
818+
/// latest splice/RBF attempt, while the first reflects the balance prior to the splice
819+
/// occurring.
820+
balance_candidates: Vec<HolderCommitmentTransactionBalance>,
821+
/// The index within [`Balance::ClaimableOnChannelClose::balance_candidates`] for the
822+
/// balance according to the current onchain state of the channel. This can be helpful when
823+
/// wanting to determine the claimable amount when the holder commitment transaction for the
824+
/// current funding transaction is broadcast and/or confirms.
825+
confirmed_balance_candidate_index: usize,
803826
/// The amount of millisatoshis which has been burned to fees from HTLCs which are outbound
804827
/// from us and are related to a payment which was sent by us. This is the sum of the
805828
/// millisatoshis part of all HTLCs which are otherwise represented by
@@ -821,7 +844,7 @@ pub enum Balance {
821844
/// to us and for which we know the preimage. This is the sum of the millisatoshis part of
822845
/// all HTLCs which would be represented by [`Balance::ContentiousClaimable`] on channel
823846
/// close, but whose current value is included in
824-
/// [`Balance::ClaimableOnChannelClose::amount_satoshis`], as well as any dust HTLCs which
847+
/// [`HolderCommitmentTransactionBalance::amount_satoshis`], as well as any dust HTLCs which
825848
/// would otherwise be represented the same.
826849
///
827850
/// This amount (rounded up to a whole satoshi value) will not be included in the counterparty's
@@ -915,6 +938,13 @@ pub enum Balance {
915938
impl Balance {
916939
/// The amount claimable, in satoshis.
917940
///
941+
/// When the channel has yet to close, this returns the balance we expect to claim from the
942+
/// channel. This may change throughout the lifetime of the channel due to payments, but also
943+
/// due to splicing. If there's a pending splice, this will return the balance we expect to have
944+
/// assuming the latest negotiated splice confirms. However, if one of the negotiated splice
945+
/// transactions has already confirmed but is not yet locked, this reports the corresponding
946+
/// balance for said splice transaction instead.
947+
///
918948
/// For outbound payments, this excludes the balance from the possible HTLC timeout.
919949
///
920950
/// For forwarded payments, this includes the balance from the possible HTLC timeout as
@@ -928,7 +958,15 @@ impl Balance {
928958
#[rustfmt::skip]
929959
pub fn claimable_amount_satoshis(&self) -> u64 {
930960
match self {
931-
Balance::ClaimableOnChannelClose { amount_satoshis, .. }|
961+
Balance::ClaimableOnChannelClose {
962+
balance_candidates, confirmed_balance_candidate_index, ..
963+
} => {
964+
if *confirmed_balance_candidate_index != 0 {
965+
balance_candidates[*confirmed_balance_candidate_index].amount_satoshis
966+
} else {
967+
balance_candidates.last().map(|balance| balance.amount_satoshis).unwrap_or(0)
968+
}
969+
},
932970
Balance::ClaimableAwaitingConfirmations { amount_satoshis, .. }|
933971
Balance::ContentiousClaimable { amount_satoshis, .. }|
934972
Balance::CounterpartyRevokedOutputClaimable { amount_satoshis, .. }
@@ -2675,7 +2713,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
26752713
debug_assert!(htlc_input_idx_opt.is_some());
26762714
BitcoinOutPoint::new(*txid, htlc_input_idx_opt.unwrap_or(0))
26772715
} else {
2678-
debug_assert!(!self.channel_type_features().supports_anchors_zero_fee_htlc_tx());
2716+
let funding = get_confirmed_funding_scope!(self);
2717+
debug_assert!(!funding.channel_type_features().supports_anchors_zero_fee_htlc_tx());
26792718
BitcoinOutPoint::new(*txid, 0)
26802719
}
26812720
} else {
@@ -2837,8 +2876,9 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
28372876
}
28382877

28392878
if let Some(txid) = confirmed_txid {
2879+
let funding_spent = get_confirmed_funding_scope!(us);
28402880
let mut found_commitment_tx = false;
2841-
if let Some(counterparty_tx_htlcs) = us.funding.counterparty_claimable_outpoints.get(&txid) {
2881+
if let Some(counterparty_tx_htlcs) = funding_spent.counterparty_claimable_outpoints.get(&txid) {
28422882
// First look for the to_remote output back to us.
28432883
if let Some(conf_thresh) = pending_commitment_tx_conf_thresh {
28442884
if let Some(value) = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| {
@@ -2859,7 +2899,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
28592899
// confirmation with the same height or have never met our dust amount.
28602900
}
28612901
}
2862-
if Some(txid) == us.funding.current_counterparty_commitment_txid || Some(txid) == us.funding.prev_counterparty_commitment_txid {
2902+
if Some(txid) == funding_spent.current_counterparty_commitment_txid || Some(txid) == funding_spent.prev_counterparty_commitment_txid {
28632903
walk_htlcs!(false, false, counterparty_tx_htlcs.iter().map(|(a, b)| (a, b.as_ref().map(|b| &**b))));
28642904
} else {
28652905
walk_htlcs!(false, true, counterparty_tx_htlcs.iter().map(|(a, b)| (a, b.as_ref().map(|b| &**b))));
@@ -2902,17 +2942,17 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
29022942
}
29032943
}
29042944
found_commitment_tx = true;
2905-
} else if txid == us.funding.current_holder_commitment_tx.trust().txid() {
2945+
} else if txid == funding_spent.current_holder_commitment_tx.trust().txid() {
29062946
walk_htlcs!(true, false, holder_commitment_htlcs!(us, CURRENT_WITH_SOURCES));
29072947
if let Some(conf_thresh) = pending_commitment_tx_conf_thresh {
29082948
res.push(Balance::ClaimableAwaitingConfirmations {
2909-
amount_satoshis: us.funding.current_holder_commitment_tx.to_broadcaster_value_sat(),
2949+
amount_satoshis: funding_spent.current_holder_commitment_tx.to_broadcaster_value_sat(),
29102950
confirmation_height: conf_thresh,
29112951
source: BalanceSource::HolderForceClosed,
29122952
});
29132953
}
29142954
found_commitment_tx = true;
2915-
} else if let Some(prev_holder_commitment_tx) = &us.funding.prev_holder_commitment_tx {
2955+
} else if let Some(prev_holder_commitment_tx) = &funding_spent.prev_holder_commitment_tx {
29162956
if txid == prev_holder_commitment_tx.trust().txid() {
29172957
walk_htlcs!(true, false, holder_commitment_htlcs!(us, PREV_WITH_SOURCES).unwrap());
29182958
if let Some(conf_thresh) = pending_commitment_tx_conf_thresh {
@@ -2931,7 +2971,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
29312971
// neither us nor our counterparty misbehaved. At worst we've under-estimated
29322972
// the amount we can claim as we'll punish a misbehaving counterparty.
29332973
res.push(Balance::ClaimableAwaitingConfirmations {
2934-
amount_satoshis: us.funding.current_holder_commitment_tx.to_broadcaster_value_sat(),
2974+
amount_satoshis: funding_spent.current_holder_commitment_tx.to_broadcaster_value_sat(),
29352975
confirmation_height: conf_thresh,
29362976
source: BalanceSource::CoopClose,
29372977
});
@@ -2943,6 +2983,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
29432983
let mut outbound_forwarded_htlc_rounded_msat = 0;
29442984
let mut inbound_claiming_htlc_rounded_msat = 0;
29452985
let mut inbound_htlc_rounded_msat = 0;
2986+
// We share the same set of HTLCs across all scopes, so we don't need to check the other
2987+
// scopes as it'd be redundant.
29462988
for (htlc, source) in holder_commitment_htlcs!(us, CURRENT_WITH_SOURCES) {
29472989
let rounded_value_msat = if htlc.transaction_output_index.is_none() {
29482990
htlc.amount_msat
@@ -2984,16 +3026,40 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
29843026
}
29853027
}
29863028
}
2987-
let to_self_value_sat = us.funding.current_holder_commitment_tx.to_broadcaster_value_sat();
3029+
let balance_candidates = core::iter::once(&us.funding)
3030+
.chain(us.pending_funding.iter())
3031+
.map(|funding| {
3032+
let to_self_value_sat = funding.current_holder_commitment_tx.to_broadcaster_value_sat();
3033+
// In addition to `commit_tx_fee_sat`, this can also include dust HTLCs, any
3034+
// elided anchors, and the total msat amount rounded down from non-dust HTLCs.
3035+
let transaction_fee_satoshis = if us.holder_pays_commitment_tx_fee.unwrap_or(true) {
3036+
let transaction = &funding.current_holder_commitment_tx.trust().built_transaction().transaction;
3037+
let output_value_sat: u64 = transaction.output.iter().map(|txout| txout.value.to_sat()).sum();
3038+
funding.channel_parameters.channel_value_satoshis - output_value_sat
3039+
} else {
3040+
0
3041+
};
3042+
HolderCommitmentTransactionBalance {
3043+
amount_satoshis: to_self_value_sat + claimable_inbound_htlc_value_sat,
3044+
transaction_fee_satoshis,
3045+
}
3046+
})
3047+
.collect();
3048+
let confirmed_balance_candidate_index = core::iter::once(&us.funding)
3049+
.chain(us.pending_funding.iter())
3050+
.enumerate()
3051+
.find(|(_, funding)| {
3052+
us.alternative_funding_confirmed
3053+
.map(|(funding_txid_confirmed, _)| funding.funding_txid() == funding_txid_confirmed)
3054+
// If `alternative_funding_confirmed` is not set, we can assume the current
3055+
// funding is confirmed.
3056+
.unwrap_or(true)
3057+
})
3058+
.map(|(idx, _)| idx)
3059+
.expect("We must have one FundingScope that is confirmed");
29883060
res.push(Balance::ClaimableOnChannelClose {
2989-
amount_satoshis: to_self_value_sat + claimable_inbound_htlc_value_sat,
2990-
// In addition to `commit_tx_fee_sat`, this can also include dust HTLCs, any elided anchors,
2991-
// and the total msat amount rounded down from non-dust HTLCs
2992-
transaction_fee_satoshis: if us.holder_pays_commitment_tx_fee.unwrap_or(true) {
2993-
let transaction = &us.funding.current_holder_commitment_tx.trust().built_transaction().transaction;
2994-
let output_value_sat: u64 = transaction.output.iter().map(|txout| txout.value.to_sat()).sum();
2995-
us.funding.channel_parameters.channel_value_satoshis - output_value_sat
2996-
} else { 0 },
3061+
balance_candidates,
3062+
confirmed_balance_candidate_index,
29973063
outbound_payment_htlc_rounded_msat,
29983064
outbound_forwarded_htlc_rounded_msat,
29993065
inbound_claiming_htlc_rounded_msat,

0 commit comments

Comments
 (0)