@@ -4290,6 +4290,55 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
42904290
42914291 self . latest_update_id = updates. update_id ;
42924292
4293+ // If a counterparty commitment update was applied while the funding output has already
4294+ // been spent on-chain, fail back the outbound HTLCs from the update. This handles the
4295+ // race where a monitor update is dispatched before the channel force-closes but only
4296+ // applied after the commitment transaction confirms.
4297+ for update in updates. updates . iter ( ) {
4298+ match update {
4299+ ChannelMonitorUpdateStep :: LatestCounterpartyCommitmentTXInfo {
4300+ htlc_outputs, ..
4301+ } => {
4302+ // Only outbound HTLCs have a source; inbound ones are `None`
4303+ // and skipped by the `filter_map`.
4304+ self . fail_htlcs_from_update_after_funding_spend (
4305+ htlc_outputs. iter ( ) . filter_map ( |( htlc, source) | {
4306+ source. as_ref ( ) . map ( |s| ( & * * s, htlc. payment_hash , htlc. amount_msat ) )
4307+ } ) ,
4308+ logger,
4309+ ) ;
4310+ } ,
4311+ ChannelMonitorUpdateStep :: LatestCounterpartyCommitment {
4312+ commitment_txs, htlc_data,
4313+ } => {
4314+ // On a counterparty commitment, `offered=false` means offered by
4315+ // us (outbound). `nondust_htlc_sources` contains sources only for
4316+ // these outbound nondust HTLCs, matching the filter order.
4317+ debug_assert_eq ! (
4318+ commitment_txs[ 0 ] . nondust_htlcs( ) . iter( )
4319+ . filter( |htlc| !htlc. offered) . count( ) ,
4320+ htlc_data. nondust_htlc_sources. len( ) ,
4321+ ) ;
4322+ let nondust = commitment_txs[ 0 ]
4323+ . nondust_htlcs ( )
4324+ . iter ( )
4325+ . filter ( |htlc| !htlc. offered )
4326+ . zip ( htlc_data. nondust_htlc_sources . iter ( ) )
4327+ . map ( |( htlc, source) | ( source, htlc. payment_hash , htlc. amount_msat ) ) ;
4328+ // Only outbound dust HTLCs have a source; inbound ones are `None`
4329+ // and skipped by the `filter_map`.
4330+ let dust = htlc_data. dust_htlcs . iter ( ) . filter_map ( |( htlc, source) | {
4331+ source. as_ref ( ) . map ( |s| ( s, htlc. payment_hash , htlc. amount_msat ) )
4332+ } ) ;
4333+ self . fail_htlcs_from_update_after_funding_spend (
4334+ nondust. chain ( dust) ,
4335+ logger,
4336+ ) ;
4337+ } ,
4338+ _ => { } ,
4339+ }
4340+ }
4341+
42934342 // Refuse updates after we've detected a spend onchain (or if the channel was otherwise
42944343 // closed), but only if the update isn't the kind of update we expect to see after channel
42954344 // closure.
@@ -4336,6 +4385,117 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
43364385 self . funding_spend_seen || self . lockdown_from_offchain || self . holder_tx_signed
43374386 }
43384387
4388+ /// Given outbound HTLCs from a counterparty commitment update, checks if the funding output
4389+ /// has been spent on-chain. If so, creates `OnchainEvent::HTLCUpdate` entries to fail back
4390+ /// HTLCs that weren't already known to the monitor.
4391+ ///
4392+ /// This handles the race where a `ChannelMonitorUpdate` with a new counterparty commitment
4393+ /// is dispatched (e.g., via deferred writes) before the channel force-closes, but only
4394+ /// applied to the in-memory monitor after the commitment transaction has already confirmed.
4395+ ///
4396+ /// Only truly new HTLCs (not present in any previously-known commitment) need to be failed
4397+ /// here. HTLCs that were already tracked by the monitor will be handled by the existing
4398+ /// `fail_unbroadcast_htlcs` logic when the spending transaction confirms.
4399+ fn fail_htlcs_from_update_after_funding_spend < ' a , L : Logger > (
4400+ & mut self , htlcs : impl Iterator < Item = ( & ' a HTLCSource , PaymentHash , u64 ) > ,
4401+ logger : & WithContext < L > ,
4402+ ) {
4403+ let pending_spend_entry = self
4404+ . onchain_events_awaiting_threshold_conf
4405+ . iter ( )
4406+ . find ( |event| matches ! ( event. event, OnchainEvent :: FundingSpendConfirmation { .. } ) )
4407+ . map ( |entry| ( entry. txid , entry. transaction . clone ( ) , entry. height , entry. block_hash ) ) ;
4408+ if self . funding_spend_confirmed . is_none ( ) && pending_spend_entry. is_none ( ) {
4409+ return ;
4410+ }
4411+
4412+ // Check HTLC sources against all previously-known commitments to find truly new
4413+ // ones. After the update has been applied, `prev_counterparty_commitment_txid` holds
4414+ // what was `current` before this update, so it represents the already-known
4415+ // counterparty state. HTLCs already present in any of these will be handled by
4416+ // `fail_unbroadcast_htlcs` when the spending transaction confirms.
4417+ let is_source_known = |source : & HTLCSource | {
4418+ if let Some ( ref txid) = self . funding . prev_counterparty_commitment_txid {
4419+ if let Some ( htlc_list) = self . funding . counterparty_claimable_outpoints . get ( txid) {
4420+ if htlc_list. iter ( ) . any ( |( _, s) | s. as_ref ( ) . map ( |s| s. as_ref ( ) ) == Some ( source) )
4421+ {
4422+ return true ;
4423+ }
4424+ }
4425+ }
4426+ // Note that we don't care about the case where a counterparty sent us a fresh local commitment transaction
4427+ // post-closure (with the `ChannelManager` still operating the channel). First of all we only care about
4428+ // resolving outbound HTLCs, which fundamentally have to be initiated by us. However we also don't mind
4429+ // looking at the current holder commitment transaction's HTLCs as any fresh outbound HTLCs will have to
4430+ // first come in a locally-initiated update to the counterparty's commitment transaction which we can, by
4431+ // refusing to apply the update, prevent the counterparty from ever seeing (as no messages can be sent until
4432+ // the monitor is updated). Thus, the HTLCs we care about can never appear in the holder commitment
4433+ // transaction.
4434+ if holder_commitment_htlcs ! ( self , CURRENT_WITH_SOURCES ) . any ( |( _, s) | s == Some ( source) )
4435+ {
4436+ return true ;
4437+ }
4438+ if let Some ( mut iter) = holder_commitment_htlcs ! ( self , PREV_WITH_SOURCES ) {
4439+ if iter. any ( |( _, s) | s == Some ( source) ) {
4440+ return true ;
4441+ }
4442+ }
4443+ false
4444+ } ;
4445+ for ( source, payment_hash, amount_msat) in htlcs {
4446+ if is_source_known ( source) {
4447+ continue ;
4448+ }
4449+ if self . counterparty_fulfilled_htlcs . get ( & SentHTLCId :: from_source ( source) ) . is_some ( ) {
4450+ continue ;
4451+ }
4452+ let htlc_value_satoshis = Some ( amount_msat / 1000 ) ;
4453+ let logger = WithContext :: from ( logger, None , None , Some ( payment_hash) ) ;
4454+ if let Some ( confirmed_txid) = self . funding_spend_confirmed {
4455+ // Funding spend already confirmed past ANTI_REORG_DELAY: resolve immediately.
4456+ log_trace ! (
4457+ logger,
4458+ "Failing HTLC from late counterparty commitment update immediately \
4459+ (funding spend already confirmed)"
4460+ ) ;
4461+ self . pending_monitor_events . push ( MonitorEvent :: HTLCEvent ( HTLCUpdate {
4462+ payment_hash,
4463+ payment_preimage : None ,
4464+ source : source. clone ( ) ,
4465+ htlc_value_satoshis,
4466+ } ) ) ;
4467+ self . htlcs_resolved_on_chain . push ( IrrevocablyResolvedHTLC {
4468+ commitment_tx_output_idx : None ,
4469+ resolving_txid : Some ( confirmed_txid) ,
4470+ resolving_tx : None ,
4471+ payment_preimage : None ,
4472+ } ) ;
4473+ } else {
4474+ // Funding spend still awaiting ANTI_REORG_DELAY: queue the failure.
4475+ let ( txid, transaction, height, block_hash) = pending_spend_entry. clone ( ) . unwrap ( ) ;
4476+ let entry = OnchainEventEntry {
4477+ txid,
4478+ transaction,
4479+ height,
4480+ block_hash,
4481+ event : OnchainEvent :: HTLCUpdate {
4482+ source : source. clone ( ) ,
4483+ payment_hash,
4484+ htlc_value_satoshis,
4485+ commitment_tx_output_idx : None ,
4486+ } ,
4487+ } ;
4488+ log_trace ! (
4489+ logger,
4490+ "Failing HTLC from late counterparty commitment update, \
4491+ waiting for confirmation (at height {})",
4492+ entry. confirmation_threshold( )
4493+ ) ;
4494+ self . onchain_events_awaiting_threshold_conf . push ( entry) ;
4495+ }
4496+ }
4497+ }
4498+
43394499 fn get_latest_update_id ( & self ) -> u64 {
43404500 self . latest_update_id
43414501 }
@@ -6760,7 +6920,7 @@ mod tests {
67606920 use bitcoin:: { Sequence , Witness } ;
67616921
67626922 use crate :: chain:: chaininterface:: LowerBoundedFeeEstimator ;
6763- use crate :: events:: ClosureReason ;
6923+ use crate :: events:: { ClosureReason , Event } ;
67646924
67656925 use super :: ChannelMonitorUpdateStep ;
67666926 use crate :: chain:: channelmonitor:: { ChannelMonitor , WithChannelMonitor } ;
@@ -6883,8 +7043,21 @@ mod tests {
68837043 check_spends ! ( htlc_txn[ 1 ] , broadcast_tx) ;
68847044
68857045 check_closed_broadcast ( & nodes[ 1 ] , 1 , true ) ;
6886- check_closed_event ( & nodes[ 1 ] , 1 , ClosureReason :: CommitmentTxConfirmed , & [ nodes[ 0 ] . node . get_our_node_id ( ) ] , 100000 ) ;
6887- check_added_monitors ( & nodes[ 1 ] , 1 ) ;
7046+ if !use_local_txn {
7047+ // When the counterparty commitment confirms, FundingSpendConfirmation matures
7048+ // immediately (no CSV delay), so funding_spend_confirmed is set. The new payment's
7049+ // commitment update then triggers immediate HTLC failure, generating payment events
7050+ // alongside the channel close event.
7051+ let events = nodes[ 1 ] . node . get_and_clear_pending_events ( ) ;
7052+ assert_eq ! ( events. len( ) , 3 ) ;
7053+ assert ! ( events. iter( ) . any( |e| matches!( e, Event :: PaymentPathFailed { .. } ) ) ) ;
7054+ assert ! ( events. iter( ) . any( |e| matches!( e, Event :: PaymentFailed { .. } ) ) ) ;
7055+ assert ! ( events. iter( ) . any( |e| matches!( e, Event :: ChannelClosed { .. } ) ) ) ;
7056+ check_added_monitors ( & nodes[ 1 ] , 2 ) ;
7057+ } else {
7058+ check_closed_event ( & nodes[ 1 ] , 1 , ClosureReason :: CommitmentTxConfirmed , & [ nodes[ 0 ] . node . get_our_node_id ( ) ] , 100000 ) ;
7059+ check_added_monitors ( & nodes[ 1 ] , 1 ) ;
7060+ }
68887061 }
68897062
68907063 #[ test]
0 commit comments