@@ -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
@@ -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,121 @@ 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+ // Defensively mark the HTLC as failed back so the expiry-based failure
4455+ // path in `block_connected` doesn't generate a duplicate `HTLCUpdate`
4456+ // event for the same source.
4457+ self . failed_back_htlc_ids . insert ( SentHTLCId :: from_source ( source) ) ;
4458+ if let Some ( confirmed_txid) = self . funding_spend_confirmed {
4459+ // Funding spend already confirmed past ANTI_REORG_DELAY: resolve immediately.
4460+ log_trace ! (
4461+ logger,
4462+ "Failing HTLC from late counterparty commitment update immediately \
4463+ (funding spend already confirmed)"
4464+ ) ;
4465+ self . pending_monitor_events . push ( MonitorEvent :: HTLCEvent ( HTLCUpdate {
4466+ payment_hash,
4467+ payment_preimage : None ,
4468+ source : source. clone ( ) ,
4469+ htlc_value_satoshis,
4470+ } ) ) ;
4471+ self . htlcs_resolved_on_chain . push ( IrrevocablyResolvedHTLC {
4472+ commitment_tx_output_idx : None ,
4473+ resolving_txid : Some ( confirmed_txid) ,
4474+ resolving_tx : None ,
4475+ payment_preimage : None ,
4476+ } ) ;
4477+ } else {
4478+ // Funding spend still awaiting ANTI_REORG_DELAY: queue the failure.
4479+ let ( txid, transaction, height, block_hash) = pending_spend_entry. clone ( ) . unwrap ( ) ;
4480+ let entry = OnchainEventEntry {
4481+ txid,
4482+ transaction,
4483+ height,
4484+ block_hash,
4485+ event : OnchainEvent :: HTLCUpdate {
4486+ source : source. clone ( ) ,
4487+ payment_hash,
4488+ htlc_value_satoshis,
4489+ commitment_tx_output_idx : None ,
4490+ } ,
4491+ } ;
4492+ log_trace ! (
4493+ logger,
4494+ "Failing HTLC from late counterparty commitment update, \
4495+ waiting for confirmation (at height {})",
4496+ entry. confirmation_threshold( )
4497+ ) ;
4498+ self . onchain_events_awaiting_threshold_conf . push ( entry) ;
4499+ }
4500+ }
4501+ }
4502+
43394503 fn get_latest_update_id ( & self ) -> u64 {
43404504 self . latest_update_id
43414505 }
@@ -6760,7 +6924,7 @@ mod tests {
67606924 use bitcoin:: { Sequence , Witness } ;
67616925
67626926 use crate :: chain:: chaininterface:: LowerBoundedFeeEstimator ;
6763- use crate :: events:: ClosureReason ;
6927+ use crate :: events:: { ClosureReason , Event } ;
67646928
67656929 use super :: ChannelMonitorUpdateStep ;
67666930 use crate :: chain:: channelmonitor:: { ChannelMonitor , WithChannelMonitor } ;
@@ -6883,8 +7047,21 @@ mod tests {
68837047 check_spends ! ( htlc_txn[ 1 ] , broadcast_tx) ;
68847048
68857049 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 ) ;
7050+ if !use_local_txn {
7051+ // When the counterparty commitment confirms, FundingSpendConfirmation matures
7052+ // immediately (no CSV delay), so funding_spend_confirmed is set. The new payment's
7053+ // commitment update then triggers immediate HTLC failure, generating payment events
7054+ // alongside the channel close event.
7055+ let events = nodes[ 1 ] . node . get_and_clear_pending_events ( ) ;
7056+ assert_eq ! ( events. len( ) , 3 ) ;
7057+ assert ! ( events. iter( ) . any( |e| matches!( e, Event :: PaymentPathFailed { .. } ) ) ) ;
7058+ assert ! ( events. iter( ) . any( |e| matches!( e, Event :: PaymentFailed { .. } ) ) ) ;
7059+ assert ! ( events. iter( ) . any( |e| matches!( e, Event :: ChannelClosed { .. } ) ) ) ;
7060+ check_added_monitors ( & nodes[ 1 ] , 2 ) ;
7061+ } else {
7062+ check_closed_event ( & nodes[ 1 ] , 1 , ClosureReason :: CommitmentTxConfirmed , & [ nodes[ 0 ] . node . get_our_node_id ( ) ] , 100000 ) ;
7063+ check_added_monitors ( & nodes[ 1 ] , 1 ) ;
7064+ }
68887065 }
68897066
68907067 #[ test]
0 commit comments