Skip to content

Fix two issues in Balance generation #2593

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 45 additions & 33 deletions lightning/src/chain/channelmonitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1685,36 +1685,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
let mut holder_timeout_spend_pending = None;
let mut htlc_spend_pending = None;
let mut holder_delayed_output_pending = None;
for event in self.onchain_events_awaiting_threshold_conf.iter() {
match event.event {
OnchainEvent::HTLCUpdate { commitment_tx_output_idx, htlc_value_satoshis, .. }
if commitment_tx_output_idx == Some(htlc_commitment_tx_output_idx) => {
debug_assert!(htlc_spend_txid_opt.is_none());
htlc_spend_txid_opt = Some(&event.txid);
debug_assert!(htlc_spend_tx_opt.is_none());
htlc_spend_tx_opt = event.transaction.as_ref();
debug_assert!(holder_timeout_spend_pending.is_none());
debug_assert_eq!(htlc_value_satoshis.unwrap(), htlc.amount_msat / 1000);
holder_timeout_spend_pending = Some(event.confirmation_threshold());
},
OnchainEvent::HTLCSpendConfirmation { commitment_tx_output_idx, preimage, .. }
if commitment_tx_output_idx == htlc_commitment_tx_output_idx => {
debug_assert!(htlc_spend_txid_opt.is_none());
htlc_spend_txid_opt = Some(&event.txid);
debug_assert!(htlc_spend_tx_opt.is_none());
htlc_spend_tx_opt = event.transaction.as_ref();
debug_assert!(htlc_spend_pending.is_none());
htlc_spend_pending = Some((event.confirmation_threshold(), preimage.is_some()));
},
OnchainEvent::MaturingOutput {
descriptor: SpendableOutputDescriptor::DelayedPaymentOutput(ref descriptor) }
if descriptor.outpoint.index as u32 == htlc_commitment_tx_output_idx => {
debug_assert!(holder_delayed_output_pending.is_none());
holder_delayed_output_pending = Some(event.confirmation_threshold());
},
_ => {},
}
}

let htlc_resolved = self.htlcs_resolved_on_chain.iter()
.find(|v| if v.commitment_tx_output_idx == Some(htlc_commitment_tx_output_idx) {
debug_assert!(htlc_spend_txid_opt.is_none());
Expand All @@ -1723,7 +1694,6 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
htlc_spend_tx_opt = v.resolving_tx.as_ref();
true
} else { false });
debug_assert!(holder_timeout_spend_pending.is_some() as u8 + htlc_spend_pending.is_some() as u8 + htlc_resolved.is_some() as u8 <= 1);

let htlc_commitment_outpoint = BitcoinOutPoint::new(confirmed_txid.unwrap(), htlc_commitment_tx_output_idx);
let htlc_output_to_spend =
Expand All @@ -1747,6 +1717,49 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
};
let htlc_output_spend_pending = self.onchain_tx_handler.is_output_spend_pending(&htlc_output_to_spend);

for event in self.onchain_events_awaiting_threshold_conf.iter() {
match event.event {
OnchainEvent::HTLCUpdate { commitment_tx_output_idx, htlc_value_satoshis, .. }
if commitment_tx_output_idx == Some(htlc_commitment_tx_output_idx) => {
debug_assert!(htlc_spend_txid_opt.is_none());
htlc_spend_txid_opt = Some(&event.txid);
debug_assert!(htlc_spend_tx_opt.is_none());
htlc_spend_tx_opt = event.transaction.as_ref();
debug_assert!(holder_timeout_spend_pending.is_none());
debug_assert_eq!(htlc_value_satoshis.unwrap(), htlc.amount_msat / 1000);
holder_timeout_spend_pending = Some(event.confirmation_threshold());
},
OnchainEvent::HTLCSpendConfirmation { commitment_tx_output_idx, preimage, .. }
if commitment_tx_output_idx == htlc_commitment_tx_output_idx => {
debug_assert!(htlc_spend_txid_opt.is_none());
htlc_spend_txid_opt = Some(&event.txid);
debug_assert!(htlc_spend_tx_opt.is_none());
htlc_spend_tx_opt = event.transaction.as_ref();
debug_assert!(htlc_spend_pending.is_none());
htlc_spend_pending = Some((event.confirmation_threshold(), preimage.is_some()));
},
OnchainEvent::MaturingOutput {
descriptor: SpendableOutputDescriptor::DelayedPaymentOutput(_) }
if event.transaction.as_ref().map(|tx| tx.input.iter()
.any(|inp| Some(inp.previous_output.txid) == confirmed_txid &&
inp.previous_output.vout == htlc_commitment_tx_output_idx))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So before this commit 259815f we will match any DelayedPaymentOutput as soon as the htlc_commitment_tx_output_idx match the transaction output. IIUC, I think such case can arise when you have a second-stage HTLC transaction revocable output with an index higher than the balance outputs (according to BOLT3 outputs ordering). Post-anchor, HTLC-timeout transaction can be aggregated so the index can be higher than 1.

The fix adds an additional check than parent commitment transaction is confirmed.

.unwrap_or(false)
=> {
debug_assert!(holder_delayed_output_pending.is_none());
holder_delayed_output_pending = Some(event.confirmation_threshold());
},
OnchainEvent::MaturingOutput {
descriptor: SpendableOutputDescriptor::DelayedPaymentOutput(ref descriptor) }
if descriptor.outpoint.into_bitcoin_outpoint() == htlc_output_to_spend => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC this path should check we’re detecting well a second-stage HTLC transaction (HTLC-timeout or HTLC-preimage) CSV encumbered delayed and return further a ClaimableAwaitingConfirmations balance event

debug_assert!(holder_delayed_output_pending.is_none());
holder_delayed_output_pending = Some(event.confirmation_threshold());
},
_ => {},
}
}

debug_assert!(holder_timeout_spend_pending.is_some() as u8 + htlc_spend_pending.is_some() as u8 + htlc_resolved.is_some() as u8 <= 1);

if let Some(conf_thresh) = holder_delayed_output_pending {
debug_assert!(holder_commitment);
return Some(Balance::ClaimableAwaitingConfirmations {
Expand Down Expand Up @@ -1849,8 +1862,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
/// confirmations on the claim transaction.
///
/// Note that for `ChannelMonitors` which track a channel which went on-chain with versions of
/// LDK prior to 0.0.111, balances may not be fully captured if our counterparty broadcasted
/// a revoked state.
/// LDK prior to 0.0.111, not all or excess balances may be included.
///
/// See [`Balance`] for additional details on the types of claimable balances which
/// may be returned here and their meanings.
Expand Down