@@ -4290,6 +4290,43 @@ 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+ self . fail_htlcs_from_update_after_funding_spend (
4303+ htlc_outputs. iter ( ) . filter_map ( |( htlc, source) | {
4304+ source. as_ref ( ) . map ( |s| ( & * * s, htlc. payment_hash , htlc. amount_msat ) )
4305+ } ) ,
4306+ logger,
4307+ ) ;
4308+ } ,
4309+ ChannelMonitorUpdateStep :: LatestCounterpartyCommitment {
4310+ commitment_txs, htlc_data,
4311+ } => {
4312+ let nondust = commitment_txs[ 0 ]
4313+ . nondust_htlcs ( )
4314+ . iter ( )
4315+ . filter ( |htlc| !htlc. offered )
4316+ . zip ( htlc_data. nondust_htlc_sources . iter ( ) )
4317+ . map ( |( htlc, source) | ( source, htlc. payment_hash , htlc. amount_msat ) ) ;
4318+ let dust = htlc_data. dust_htlcs . iter ( ) . filter_map ( |( htlc, source) | {
4319+ source. as_ref ( ) . map ( |s| ( s, htlc. payment_hash , htlc. amount_msat ) )
4320+ } ) ;
4321+ self . fail_htlcs_from_update_after_funding_spend (
4322+ nondust. chain ( dust) ,
4323+ logger,
4324+ ) ;
4325+ } ,
4326+ _ => { } ,
4327+ }
4328+ }
4329+
42934330 // Refuse updates after we've detected a spend onchain (or if the channel was otherwise
42944331 // closed), but only if the update isn't the kind of update we expect to see after channel
42954332 // closure.
@@ -4336,6 +4373,117 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
43364373 self . funding_spend_seen || self . lockdown_from_offchain || self . holder_tx_signed
43374374 }
43384375
4376+ /// Given outbound HTLCs from a counterparty commitment update, checks if the funding output
4377+ /// has been spent on-chain. If so, creates `OnchainEvent::HTLCUpdate` entries to fail back
4378+ /// HTLCs that weren't already known to the monitor.
4379+ ///
4380+ /// This handles the race where a `ChannelMonitorUpdate` with a new counterparty commitment
4381+ /// is dispatched (e.g., via deferred writes) before the channel force-closes, but only
4382+ /// applied to the in-memory monitor after the commitment transaction has already confirmed.
4383+ ///
4384+ /// Only truly new HTLCs (not present in any previously-known commitment) need to be failed
4385+ /// here. HTLCs that were already tracked by the monitor will be handled by the existing
4386+ /// `fail_unbroadcast_htlcs` logic when the spending transaction confirms.
4387+ fn fail_htlcs_from_update_after_funding_spend < ' a , L : Logger > (
4388+ & mut self , htlcs : impl Iterator < Item = ( & ' a HTLCSource , PaymentHash , u64 ) > ,
4389+ logger : & WithContext < L > ,
4390+ ) {
4391+ let pending_spend_entry = self
4392+ . onchain_events_awaiting_threshold_conf
4393+ . iter ( )
4394+ . find ( |event| matches ! ( event. event, OnchainEvent :: FundingSpendConfirmation { .. } ) )
4395+ . map ( |entry| ( entry. txid , entry. transaction . clone ( ) , entry. height , entry. block_hash ) ) ;
4396+ if self . funding_spend_confirmed . is_none ( ) && pending_spend_entry. is_none ( ) {
4397+ return ;
4398+ }
4399+
4400+ // Check HTLC sources against all previously-known commitments to find truly new
4401+ // ones. After the update has been applied, `prev_counterparty_commitment_txid` holds
4402+ // what was `current` before this update, so it represents the already-known
4403+ // counterparty state. HTLCs already present in any of these will be handled by
4404+ // `fail_unbroadcast_htlcs` when the spending transaction confirms.
4405+ let is_source_known = |source : & HTLCSource | {
4406+ if let Some ( ref txid) = self . funding . prev_counterparty_commitment_txid {
4407+ if let Some ( htlc_list) = self . funding . counterparty_claimable_outpoints . get ( txid) {
4408+ if htlc_list. iter ( ) . any ( |( _, s) | s. as_ref ( ) . map ( |s| s. as_ref ( ) ) == Some ( source) )
4409+ {
4410+ return true ;
4411+ }
4412+ }
4413+ }
4414+ // Note that we don't care about the case where a counterparty sent us a fresh local commitment transaction
4415+ // post-closure (with the `ChannelManager` still operating the channel). First of all we only care about
4416+ // resolving outbound HTLCs, which fundamentally have to be initiated by us. However we also don't mind
4417+ // looking at the current holder commitment transaction's HTLCs as any fresh outbound HTLCs will have to
4418+ // first come in a locally-initiated update to the counterparty's commitment transaction which we can, by
4419+ // refusing to apply the update, prevent the counterparty from ever seeing (as no messages can be sent until
4420+ // the monitor is updated). Thus, the HTLCs we care about can never appear in the holder commitment
4421+ // transaction.
4422+ if holder_commitment_htlcs ! ( self , CURRENT_WITH_SOURCES ) . any ( |( _, s) | s == Some ( source) )
4423+ {
4424+ return true ;
4425+ }
4426+ if let Some ( mut iter) = holder_commitment_htlcs ! ( self , PREV_WITH_SOURCES ) {
4427+ if iter. any ( |( _, s) | s == Some ( source) ) {
4428+ return true ;
4429+ }
4430+ }
4431+ false
4432+ } ;
4433+ for ( source, payment_hash, amount_msat) in htlcs {
4434+ if is_source_known ( source) {
4435+ continue ;
4436+ }
4437+ if self . counterparty_fulfilled_htlcs . get ( & SentHTLCId :: from_source ( source) ) . is_some ( ) {
4438+ continue ;
4439+ }
4440+ let htlc_value_satoshis = Some ( amount_msat / 1000 ) ;
4441+ let logger = WithContext :: from ( logger, None , None , Some ( payment_hash) ) ;
4442+ if let Some ( confirmed_txid) = self . funding_spend_confirmed {
4443+ // Funding spend already confirmed past ANTI_REORG_DELAY: resolve immediately.
4444+ log_trace ! (
4445+ logger,
4446+ "Failing HTLC from late counterparty commitment update immediately \
4447+ (funding spend already confirmed)"
4448+ ) ;
4449+ self . pending_monitor_events . push ( MonitorEvent :: HTLCEvent ( HTLCUpdate {
4450+ payment_hash,
4451+ payment_preimage : None ,
4452+ source : source. clone ( ) ,
4453+ htlc_value_satoshis,
4454+ } ) ) ;
4455+ self . htlcs_resolved_on_chain . push ( IrrevocablyResolvedHTLC {
4456+ commitment_tx_output_idx : None ,
4457+ resolving_txid : Some ( confirmed_txid) ,
4458+ resolving_tx : None ,
4459+ payment_preimage : None ,
4460+ } ) ;
4461+ } else {
4462+ // Funding spend still awaiting ANTI_REORG_DELAY: queue the failure.
4463+ let ( txid, transaction, height, block_hash) = pending_spend_entry. clone ( ) . unwrap ( ) ;
4464+ let entry = OnchainEventEntry {
4465+ txid,
4466+ transaction,
4467+ height,
4468+ block_hash,
4469+ event : OnchainEvent :: HTLCUpdate {
4470+ source : source. clone ( ) ,
4471+ payment_hash,
4472+ htlc_value_satoshis,
4473+ commitment_tx_output_idx : None ,
4474+ } ,
4475+ } ;
4476+ log_trace ! (
4477+ logger,
4478+ "Failing HTLC from late counterparty commitment update, \
4479+ waiting for confirmation (at height {})",
4480+ entry. confirmation_threshold( )
4481+ ) ;
4482+ self . onchain_events_awaiting_threshold_conf . push ( entry) ;
4483+ }
4484+ }
4485+ }
4486+
43394487 fn get_latest_update_id ( & self ) -> u64 {
43404488 self . latest_update_id
43414489 }
@@ -6760,7 +6908,7 @@ mod tests {
67606908 use bitcoin:: { Sequence , Witness } ;
67616909
67626910 use crate :: chain:: chaininterface:: LowerBoundedFeeEstimator ;
6763- use crate :: events:: ClosureReason ;
6911+ use crate :: events:: { ClosureReason , Event } ;
67646912
67656913 use super :: ChannelMonitorUpdateStep ;
67666914 use crate :: chain:: channelmonitor:: { ChannelMonitor , WithChannelMonitor } ;
@@ -6883,8 +7031,21 @@ mod tests {
68837031 check_spends ! ( htlc_txn[ 1 ] , broadcast_tx) ;
68847032
68857033 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 ) ;
7034+ if !use_local_txn {
7035+ // When the counterparty commitment confirms, FundingSpendConfirmation matures
7036+ // immediately (no CSV delay), so funding_spend_confirmed is set. The new payment's
7037+ // commitment update then triggers immediate HTLC failure, generating payment events
7038+ // alongside the channel close event.
7039+ let events = nodes[ 1 ] . node . get_and_clear_pending_events ( ) ;
7040+ assert_eq ! ( events. len( ) , 3 ) ;
7041+ assert ! ( events. iter( ) . any( |e| matches!( e, Event :: PaymentPathFailed { .. } ) ) ) ;
7042+ assert ! ( events. iter( ) . any( |e| matches!( e, Event :: PaymentFailed { .. } ) ) ) ;
7043+ assert ! ( events. iter( ) . any( |e| matches!( e, Event :: ChannelClosed { .. } ) ) ) ;
7044+ check_added_monitors ( & nodes[ 1 ] , 2 ) ;
7045+ } else {
7046+ check_closed_event ( & nodes[ 1 ] , 1 , ClosureReason :: CommitmentTxConfirmed , & [ nodes[ 0 ] . node . get_our_node_id ( ) ] , 100000 ) ;
7047+ check_added_monitors ( & nodes[ 1 ] , 1 ) ;
7048+ }
68887049 }
68897050
68907051 #[ test]
0 commit comments