@@ -1378,8 +1378,8 @@ pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
13781378 /// In-memory only HTLC ids used to track upstream HTLCs that have been failed backwards due to
13791379 /// a downstream channel force-close remaining unconfirmed by the time the upstream timeout
13801380 /// expires. This is used to tell us we already generated an event to fail this HTLC back
1381- /// during a previous block scan.
1382- failed_back_htlc_ids : HashSet < SentHTLCId > ,
1381+ /// during a previous block scan. Not serialized.
1382+ pub ( crate ) failed_back_htlc_ids : HashSet < SentHTLCId > ,
13831383
13841384 // The auxiliary HTLC data associated with a holder commitment transaction. This includes
13851385 // non-dust HTLC sources, along with dust HTLCs and their sources. Note that this assumes any
@@ -4299,6 +4299,55 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
42994299
43004300 self . latest_update_id = updates. update_id ;
43014301
4302+ // If a counterparty commitment update was applied while the funding output has already
4303+ // been spent on-chain, fail back the outbound HTLCs from the update. This handles the
4304+ // race where a monitor update is dispatched before the channel force-closes but only
4305+ // applied after the commitment transaction confirms.
4306+ for update in updates. updates . iter ( ) {
4307+ match update {
4308+ ChannelMonitorUpdateStep :: LatestCounterpartyCommitmentTXInfo {
4309+ htlc_outputs, ..
4310+ } => {
4311+ // Only outbound HTLCs have a source; inbound ones are `None`
4312+ // and skipped by the `filter_map`.
4313+ self . fail_htlcs_from_update_after_funding_spend (
4314+ htlc_outputs. iter ( ) . filter_map ( |( htlc, source) | {
4315+ source. as_ref ( ) . map ( |s| ( & * * s, htlc. payment_hash , htlc. amount_msat ) )
4316+ } ) ,
4317+ logger,
4318+ ) ;
4319+ } ,
4320+ ChannelMonitorUpdateStep :: LatestCounterpartyCommitment {
4321+ commitment_txs, htlc_data,
4322+ } => {
4323+ // On a counterparty commitment, `offered=false` means offered by
4324+ // us (outbound). `nondust_htlc_sources` contains sources only for
4325+ // these outbound nondust HTLCs, matching the filter order.
4326+ debug_assert_eq ! (
4327+ commitment_txs[ 0 ] . nondust_htlcs( ) . iter( )
4328+ . filter( |htlc| !htlc. offered) . count( ) ,
4329+ htlc_data. nondust_htlc_sources. len( ) ,
4330+ ) ;
4331+ let nondust = commitment_txs[ 0 ]
4332+ . nondust_htlcs ( )
4333+ . iter ( )
4334+ . filter ( |htlc| !htlc. offered )
4335+ . zip ( htlc_data. nondust_htlc_sources . iter ( ) )
4336+ . map ( |( htlc, source) | ( source, htlc. payment_hash , htlc. amount_msat ) ) ;
4337+ // Only outbound dust HTLCs have a source; inbound ones are `None`
4338+ // and skipped by the `filter_map`.
4339+ let dust = htlc_data. dust_htlcs . iter ( ) . filter_map ( |( htlc, source) | {
4340+ source. as_ref ( ) . map ( |s| ( s, htlc. payment_hash , htlc. amount_msat ) )
4341+ } ) ;
4342+ self . fail_htlcs_from_update_after_funding_spend (
4343+ nondust. chain ( dust) ,
4344+ logger,
4345+ ) ;
4346+ } ,
4347+ _ => { } ,
4348+ }
4349+ }
4350+
43024351 // Refuse updates after we've detected a spend onchain (or if the channel was otherwise
43034352 // closed), but only if the update isn't the kind of update we expect to see after channel
43044353 // closure.
@@ -4345,6 +4394,121 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
43454394 self . funding_spend_seen || self . lockdown_from_offchain || self . holder_tx_signed
43464395 }
43474396
4397+ /// Given outbound HTLCs from a counterparty commitment update, checks if the funding output
4398+ /// has been spent on-chain. If so, creates `OnchainEvent::HTLCUpdate` entries to fail back
4399+ /// HTLCs that weren't already known to the monitor.
4400+ ///
4401+ /// This handles the race where a `ChannelMonitorUpdate` with a new counterparty commitment
4402+ /// is dispatched (e.g., via deferred writes) before the channel force-closes, but only
4403+ /// applied to the in-memory monitor after the commitment transaction has already confirmed.
4404+ ///
4405+ /// Only truly new HTLCs (not present in any previously-known commitment) need to be failed
4406+ /// here. HTLCs that were already tracked by the monitor will be handled by the existing
4407+ /// `fail_unbroadcast_htlcs` logic when the spending transaction confirms.
4408+ fn fail_htlcs_from_update_after_funding_spend < ' a , L : Logger > (
4409+ & mut self , htlcs : impl Iterator < Item = ( & ' a HTLCSource , PaymentHash , u64 ) > ,
4410+ logger : & WithContext < L > ,
4411+ ) {
4412+ let pending_spend_entry = self
4413+ . onchain_events_awaiting_threshold_conf
4414+ . iter ( )
4415+ . find ( |event| matches ! ( event. event, OnchainEvent :: FundingSpendConfirmation { .. } ) )
4416+ . map ( |entry| ( entry. txid , entry. transaction . clone ( ) , entry. height , entry. block_hash ) ) ;
4417+ if self . funding_spend_confirmed . is_none ( ) && pending_spend_entry. is_none ( ) {
4418+ return ;
4419+ }
4420+
4421+ // Check HTLC sources against all previously-known commitments to find truly new
4422+ // ones. After the update has been applied, `prev_counterparty_commitment_txid` holds
4423+ // what was `current` before this update, so it represents the already-known
4424+ // counterparty state. HTLCs already present in any of these will be handled by
4425+ // `fail_unbroadcast_htlcs` when the spending transaction confirms.
4426+ let is_source_known = |source : & HTLCSource | {
4427+ if let Some ( ref txid) = self . funding . prev_counterparty_commitment_txid {
4428+ if let Some ( htlc_list) = self . funding . counterparty_claimable_outpoints . get ( txid) {
4429+ if htlc_list. iter ( ) . any ( |( _, s) | s. as_ref ( ) . map ( |s| s. as_ref ( ) ) == Some ( source) )
4430+ {
4431+ return true ;
4432+ }
4433+ }
4434+ }
4435+ // Note that we don't care about the case where a counterparty sent us a fresh local commitment transaction
4436+ // post-closure (with the `ChannelManager` still operating the channel). First of all we only care about
4437+ // resolving outbound HTLCs, which fundamentally have to be initiated by us. However we also don't mind
4438+ // looking at the current holder commitment transaction's HTLCs as any fresh outbound HTLCs will have to
4439+ // first come in a locally-initiated update to the counterparty's commitment transaction which we can, by
4440+ // refusing to apply the update, prevent the counterparty from ever seeing (as no messages can be sent until
4441+ // the monitor is updated). Thus, the HTLCs we care about can never appear in the holder commitment
4442+ // transaction.
4443+ if holder_commitment_htlcs ! ( self , CURRENT_WITH_SOURCES ) . any ( |( _, s) | s == Some ( source) )
4444+ {
4445+ return true ;
4446+ }
4447+ if let Some ( mut iter) = holder_commitment_htlcs ! ( self , PREV_WITH_SOURCES ) {
4448+ if iter. any ( |( _, s) | s == Some ( source) ) {
4449+ return true ;
4450+ }
4451+ }
4452+ false
4453+ } ;
4454+ for ( source, payment_hash, amount_msat) in htlcs {
4455+ if is_source_known ( source) {
4456+ continue ;
4457+ }
4458+ if self . counterparty_fulfilled_htlcs . get ( & SentHTLCId :: from_source ( source) ) . is_some ( ) {
4459+ continue ;
4460+ }
4461+ let htlc_value_satoshis = Some ( amount_msat / 1000 ) ;
4462+ let logger = WithContext :: from ( logger, None , None , Some ( payment_hash) ) ;
4463+ // Defensively mark the HTLC as failed back so the expiry-based failure
4464+ // path in `block_connected` doesn't generate a duplicate `HTLCUpdate`
4465+ // event for the same source.
4466+ self . failed_back_htlc_ids . insert ( SentHTLCId :: from_source ( source) ) ;
4467+ if let Some ( confirmed_txid) = self . funding_spend_confirmed {
4468+ // Funding spend already confirmed past ANTI_REORG_DELAY: resolve immediately.
4469+ log_trace ! (
4470+ logger,
4471+ "Failing HTLC from late counterparty commitment update immediately \
4472+ (funding spend already confirmed)"
4473+ ) ;
4474+ self . pending_monitor_events . push ( MonitorEvent :: HTLCEvent ( HTLCUpdate {
4475+ payment_hash,
4476+ payment_preimage : None ,
4477+ source : source. clone ( ) ,
4478+ htlc_value_satoshis,
4479+ } ) ) ;
4480+ self . htlcs_resolved_on_chain . push ( IrrevocablyResolvedHTLC {
4481+ commitment_tx_output_idx : None ,
4482+ resolving_txid : Some ( confirmed_txid) ,
4483+ resolving_tx : None ,
4484+ payment_preimage : None ,
4485+ } ) ;
4486+ } else {
4487+ // Funding spend still awaiting ANTI_REORG_DELAY: queue the failure.
4488+ let ( txid, transaction, height, block_hash) = pending_spend_entry. clone ( ) . unwrap ( ) ;
4489+ let entry = OnchainEventEntry {
4490+ txid,
4491+ transaction,
4492+ height,
4493+ block_hash,
4494+ event : OnchainEvent :: HTLCUpdate {
4495+ source : source. clone ( ) ,
4496+ payment_hash,
4497+ htlc_value_satoshis,
4498+ commitment_tx_output_idx : None ,
4499+ } ,
4500+ } ;
4501+ log_trace ! (
4502+ logger,
4503+ "Failing HTLC from late counterparty commitment update, \
4504+ waiting for confirmation (at height {})",
4505+ entry. confirmation_threshold( )
4506+ ) ;
4507+ self . onchain_events_awaiting_threshold_conf . push ( entry) ;
4508+ }
4509+ }
4510+ }
4511+
43484512 fn get_latest_update_id ( & self ) -> u64 {
43494513 self . latest_update_id
43504514 }
@@ -6834,7 +6998,7 @@ mod tests {
68346998 use bitcoin:: { Sequence , Witness } ;
68356999
68367000 use crate :: chain:: chaininterface:: LowerBoundedFeeEstimator ;
6837- use crate :: events:: ClosureReason ;
7001+ use crate :: events:: { ClosureReason , Event } ;
68387002
68397003 use super :: ChannelMonitorUpdateStep ;
68407004 use crate :: chain:: channelmonitor:: { ChannelMonitor , WithChannelMonitor } ;
@@ -6957,8 +7121,21 @@ mod tests {
69577121 check_spends ! ( htlc_txn[ 1 ] , broadcast_tx) ;
69587122
69597123 check_closed_broadcast ( & nodes[ 1 ] , 1 , true ) ;
6960- check_closed_event ( & nodes[ 1 ] , 1 , ClosureReason :: CommitmentTxConfirmed , & [ nodes[ 0 ] . node . get_our_node_id ( ) ] , 100000 ) ;
6961- check_added_monitors ( & nodes[ 1 ] , 1 ) ;
7124+ if !use_local_txn {
7125+ // When the counterparty commitment confirms, FundingSpendConfirmation matures
7126+ // immediately (no CSV delay), so funding_spend_confirmed is set. The new payment's
7127+ // commitment update then triggers immediate HTLC failure, generating payment events
7128+ // alongside the channel close event.
7129+ let events = nodes[ 1 ] . node . get_and_clear_pending_events ( ) ;
7130+ assert_eq ! ( events. len( ) , 3 ) ;
7131+ assert ! ( events. iter( ) . any( |e| matches!( e, Event :: PaymentPathFailed { .. } ) ) ) ;
7132+ assert ! ( events. iter( ) . any( |e| matches!( e, Event :: PaymentFailed { .. } ) ) ) ;
7133+ assert ! ( events. iter( ) . any( |e| matches!( e, Event :: ChannelClosed { .. } ) ) ) ;
7134+ check_added_monitors ( & nodes[ 1 ] , 2 ) ;
7135+ } else {
7136+ check_closed_event ( & nodes[ 1 ] , 1 , ClosureReason :: CommitmentTxConfirmed , & [ nodes[ 0 ] . node . get_our_node_id ( ) ] , 100000 ) ;
7137+ check_added_monitors ( & nodes[ 1 ] , 1 ) ;
7138+ }
69627139 }
69637140
69647141 #[ test]
0 commit comments