@@ -1757,6 +1757,22 @@ impl OutboundPayments {
17571757 true
17581758 }
17591759 } ,
1760+ PendingOutboundPayment :: StaticInvoiceReceived { route_params, payment_hash, .. } => {
1761+ let is_stale =
1762+ route_params. payment_params . expiry_time . unwrap_or_else ( u64:: max_value) <
1763+ duration_since_epoch. as_secs ( ) ;
1764+ if is_stale {
1765+ let fail_ev = events:: Event :: PaymentFailed {
1766+ payment_id : * payment_id,
1767+ payment_hash : * payment_hash,
1768+ reason : Some ( PaymentFailureReason :: PaymentExpired )
1769+ } ;
1770+ pending_events. push_back ( ( fail_ev, None ) ) ;
1771+ false
1772+ } else {
1773+ true
1774+ }
1775+ } ,
17601776 _ => true ,
17611777 } ) ;
17621778 }
@@ -2008,11 +2024,11 @@ mod tests {
20082024
20092025 use crate :: blinded_path:: EmptyNodeIdLookUp ;
20102026 use crate :: events:: { Event , PathFailure , PaymentFailureReason } ;
2011- use crate :: ln:: types:: PaymentHash ;
2027+ use crate :: ln:: types:: { PaymentHash , PaymentPreimage } ;
20122028 use crate :: ln:: channelmanager:: { PaymentId , RecipientOnionFields } ;
20132029 use crate :: ln:: features:: { ChannelFeatures , NodeFeatures } ;
20142030 use crate :: ln:: msgs:: { ErrorAction , LightningError } ;
2015- use crate :: ln:: outbound_payment:: { Bolt12PaymentError , OutboundPayments , Retry , RetryableSendFailure , StaleExpiration } ;
2031+ use crate :: ln:: outbound_payment:: { Bolt12PaymentError , OutboundPayments , PendingOutboundPayment , Retry , RetryableSendFailure , StaleExpiration } ;
20162032 #[ cfg( feature = "std" ) ]
20172033 use crate :: offers:: invoice:: DEFAULT_RELATIVE_EXPIRY ;
20182034 use crate :: offers:: offer:: OfferBuilder ;
@@ -2551,4 +2567,51 @@ mod tests {
25512567 assert ! ( outbound_payments. has_pending_payments( ) ) ;
25522568 assert ! ( pending_events. lock( ) . unwrap( ) . is_empty( ) ) ;
25532569 }
2570+
2571+ #[ test]
2572+ fn time_out_unreleased_async_payments ( ) {
2573+ let pending_events = Mutex :: new ( VecDeque :: new ( ) ) ;
2574+ let outbound_payments = OutboundPayments :: new ( ) ;
2575+ let payment_id = PaymentId ( [ 0 ; 32 ] ) ;
2576+ let absolute_expiry = 60 ;
2577+
2578+ let mut outbounds = outbound_payments. pending_outbound_payments . lock ( ) . unwrap ( ) ;
2579+ let payment_params = PaymentParameters :: from_node_id ( test_utils:: pubkey ( 42 ) , 0 )
2580+ . with_expiry_time ( absolute_expiry) ;
2581+ let route_params = RouteParameters {
2582+ payment_params,
2583+ final_value_msat : 0 ,
2584+ max_total_routing_fee_msat : None ,
2585+ } ;
2586+ let payment_hash = PaymentHash ( [ 0 ; 32 ] ) ;
2587+ let outbound = PendingOutboundPayment :: StaticInvoiceReceived {
2588+ payment_hash,
2589+ keysend_preimage : PaymentPreimage ( [ 0 ; 32 ] ) ,
2590+ retry_strategy : Retry :: Attempts ( 0 ) ,
2591+ payment_release_secret : [ 0 ; 32 ] ,
2592+ route_params,
2593+ } ;
2594+ outbounds. insert ( payment_id, outbound) ;
2595+ core:: mem:: drop ( outbounds) ;
2596+
2597+ // The payment will not be removed if it isn't expired yet.
2598+ outbound_payments. remove_stale_payments ( Duration :: from_secs ( absolute_expiry) , & pending_events) ;
2599+ let outbounds = outbound_payments. pending_outbound_payments . lock ( ) . unwrap ( ) ;
2600+ assert_eq ! ( outbounds. len( ) , 1 ) ;
2601+ let events = pending_events. lock ( ) . unwrap ( ) ;
2602+ assert_eq ! ( events. len( ) , 0 ) ;
2603+ core:: mem:: drop ( outbounds) ;
2604+ core:: mem:: drop ( events) ;
2605+
2606+ outbound_payments. remove_stale_payments ( Duration :: from_secs ( absolute_expiry + 1 ) , & pending_events) ;
2607+ let outbounds = outbound_payments. pending_outbound_payments . lock ( ) . unwrap ( ) ;
2608+ assert_eq ! ( outbounds. len( ) , 0 ) ;
2609+ let events = pending_events. lock ( ) . unwrap ( ) ;
2610+ assert_eq ! ( events. len( ) , 1 ) ;
2611+ assert_eq ! ( events[ 0 ] , ( Event :: PaymentFailed {
2612+ payment_hash,
2613+ payment_id,
2614+ reason: Some ( PaymentFailureReason :: PaymentExpired ) ,
2615+ } , None ) ) ;
2616+ }
25542617}
0 commit comments