@@ -1287,7 +1287,7 @@ pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
1287
1287
/// a downstream channel force-close remaining unconfirmed by the time the upstream timeout
1288
1288
/// expires. This is used to tell us we already generated an event to fail this HTLC back
1289
1289
/// during a previous block scan.
1290
- failed_back_htlc_ids : HashSet < SentHTLCId > ,
1290
+ failed_back_htlc_ids : HashMap < SentHTLCId , u64 > ,
1291
1291
1292
1292
// The auxiliary HTLC data associated with a holder commitment transaction. This includes
1293
1293
// non-dust HTLC sources, along with dust HTLCs and their sources. Note that this assumes any
@@ -1885,7 +1885,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
1885
1885
initial_counterparty_commitment_tx : None ,
1886
1886
balances_empty_height : None ,
1887
1887
1888
- failed_back_htlc_ids : new_hash_set ( ) ,
1888
+ failed_back_htlc_ids : new_hash_map ( ) ,
1889
1889
1890
1890
// There are never any HTLCs in the initial commitment transaction
1891
1891
current_holder_htlc_data : CommitmentHTLCData :: new ( ) ,
@@ -5435,7 +5435,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
5435
5435
let mut matured_htlcs = Vec :: new ( ) ;
5436
5436
5437
5437
// Produce actionable events from on-chain events having reached their threshold.
5438
- for entry in onchain_events_reaching_threshold_conf {
5438
+ for entry in onchain_events_reaching_threshold_conf. clone ( ) {
5439
5439
match entry. event {
5440
5440
OnchainEvent :: HTLCUpdate { source, payment_hash, htlc_value_satoshis, commitment_tx_output_idx } => {
5441
5441
// Check for duplicate HTLC resolutions.
@@ -5502,6 +5502,81 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
5502
5502
}
5503
5503
}
5504
5504
5505
+ // Immediate fail-back on stale force-close, regardless of expiry or whether we're allowed to send further updates.
5506
+ let current_counterparty_htlcs = if let Some ( txid) = self . funding . current_counterparty_commitment_txid {
5507
+ if let Some ( htlc_outputs) = self . funding . counterparty_claimable_outpoints . get ( & txid) {
5508
+ Some ( htlc_outputs. iter ( ) . map ( |& ( ref a, ref b) | ( a, b. as_ref ( ) . map ( |boxed| & * * boxed) ) ) )
5509
+ } else { None }
5510
+ } else { None } . into_iter ( ) . flatten ( ) ;
5511
+
5512
+ let prev_counterparty_htlcs = if let Some ( txid) = self . funding . prev_counterparty_commitment_txid {
5513
+ if let Some ( htlc_outputs) = self . funding . counterparty_claimable_outpoints . get ( & txid) {
5514
+ Some ( htlc_outputs. iter ( ) . map ( |& ( ref a, ref b) | ( a, b. as_ref ( ) . map ( |boxed| & * * boxed) ) ) )
5515
+ } else { None }
5516
+ } else { None } . into_iter ( ) . flatten ( ) ;
5517
+
5518
+ let htlcs = holder_commitment_htlcs ! ( self , CURRENT_WITH_SOURCES )
5519
+ . chain ( current_counterparty_htlcs)
5520
+ . chain ( prev_counterparty_htlcs) ;
5521
+
5522
+ // To correctly handle duplicate HTLCs, we first count all expected instances from
5523
+ // the commitment transactions.
5524
+ let mut expected_htlc_counts: HashMap < SentHTLCId , u64 > = new_hash_map ( ) ;
5525
+ for ( _, source_opt) in htlcs. clone ( ) {
5526
+ if let Some ( source) = source_opt {
5527
+ * expected_htlc_counts. entry ( SentHTLCId :: from_source ( source) ) . or_default ( ) += 1 ;
5528
+ }
5529
+ }
5530
+
5531
+ // Get a lookup of all HTLCs the monitor is currently tracking on-chain.
5532
+ let monitor_htlc_sources: HashSet < & HTLCSource > = onchain_events_reaching_threshold_conf
5533
+ . iter ( )
5534
+ . filter_map ( |event_entry| match & event_entry. event {
5535
+ OnchainEvent :: HTLCUpdate { source, .. } => Some ( source) ,
5536
+ _ => None ,
5537
+ } )
5538
+ . collect ( ) ;
5539
+
5540
+ // Group all in-flight HTLCs by payment hash to handle duplicates correctly.
5541
+ let mut htlcs_by_hash: HashMap < PaymentHash , Vec < ( & HTLCOutputInCommitment , & HTLCSource ) > > = new_hash_map ( ) ;
5542
+ for ( htlc, source_opt) in htlcs {
5543
+ if let Some ( source) = source_opt {
5544
+ htlcs_by_hash. entry ( htlc. payment_hash ) . or_default ( ) . push ( ( htlc, source) ) ;
5545
+ }
5546
+ }
5547
+
5548
+ for ( payment_hash, htlc_group) in htlcs_by_hash {
5549
+ // If any HTLC in this group is missing from the monitor's on-chain view,
5550
+ // it indicates a stale state was used. We must fail back the entire group.
5551
+ let is_any_htlc_missing = htlc_group
5552
+ . iter ( )
5553
+ . any ( |( _, source) | !monitor_htlc_sources. contains ( source) ) ;
5554
+ if is_any_htlc_missing {
5555
+ log_info ! ( logger,
5556
+ "Detected stale force-close. Failing back HTLCs for hash {}." ,
5557
+ & payment_hash) ;
5558
+ for ( htlc, source) in htlc_group {
5559
+ let htlc_id = SentHTLCId :: from_source ( source) ;
5560
+ let already_failed_count = * self . failed_back_htlc_ids . get ( & htlc_id) . unwrap_or ( & 0 ) ;
5561
+ let expected_count = * expected_htlc_counts. get ( & htlc_id) . unwrap_or ( & 0 ) ;
5562
+
5563
+ // Only fail back if we haven't already failed all expected instances.
5564
+ if already_failed_count < expected_count {
5565
+ log_error ! ( logger,
5566
+ "Failing back HTLC for payment {} due to stale close." ,
5567
+ log_bytes!( payment_hash. 0 ) ) ;
5568
+ self . pending_monitor_events . push ( MonitorEvent :: HTLCEvent ( HTLCUpdate {
5569
+ source : source. clone ( ) ,
5570
+ payment_preimage : None ,
5571
+ payment_hash,
5572
+ htlc_value_satoshis : Some ( htlc. amount_msat / 1000 ) ,
5573
+ } ) ) ;
5574
+ * self . failed_back_htlc_ids . entry ( htlc_id) . or_default ( ) += 1 ;
5575
+ }
5576
+ }
5577
+ }
5578
+ }
5579
+
5505
5580
if self . no_further_updates_allowed ( ) {
5506
5581
// Fail back HTLCs on backwards channels if they expire within
5507
5582
// `LATENCY_GRACE_PERIOD_BLOCKS` blocks and the channel is closed (i.e. we're at a
@@ -5547,7 +5622,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
5547
5622
if duplicate_event {
5548
5623
continue ;
5549
5624
}
5550
- if !self . failed_back_htlc_ids . insert ( SentHTLCId :: from_source ( source) ) {
5625
+ let htlc_id = SentHTLCId :: from_source ( source) ;
5626
+ if * self . failed_back_htlc_ids . get ( & htlc_id) . unwrap_or ( & 0 ) > 0 {
5551
5627
continue ;
5552
5628
}
5553
5629
if !duplicate_event {
@@ -5560,6 +5636,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
5560
5636
payment_hash : htlc. payment_hash ,
5561
5637
htlc_value_satoshis : Some ( htlc. amount_msat / 1000 ) ,
5562
5638
} ) ) ;
5639
+ * self . failed_back_htlc_ids . entry ( htlc_id) . or_default ( ) += 1 ;
5563
5640
}
5564
5641
}
5565
5642
}
@@ -6546,7 +6623,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
6546
6623
initial_counterparty_commitment_info,
6547
6624
initial_counterparty_commitment_tx,
6548
6625
balances_empty_height,
6549
- failed_back_htlc_ids : new_hash_set ( ) ,
6626
+ failed_back_htlc_ids : new_hash_map ( ) ,
6550
6627
6551
6628
current_holder_htlc_data,
6552
6629
prev_holder_htlc_data,
0 commit comments