@@ -47,7 +47,7 @@ pub(crate) enum PendingOutboundPayment {
4747 session_privs : HashSet < [ u8 ; 32 ] > ,
4848 } ,
4949 AwaitingInvoice {
50- absolute_expiry : Duration ,
50+ expiration : StaleExpiration ,
5151 retry_strategy : Retry ,
5252 max_total_routing_fee_msat : Option < u64 > ,
5353 } ,
@@ -378,6 +378,22 @@ impl<T: Time> Display for PaymentAttemptsUsingTime<T> {
378378 }
379379}
380380
381+ /// How long before a [`PendingOutboundPayment::AwaitingInvoice`] should be considered stale and
382+ /// candidate for removal in [`OutboundPayments::remove_stale_payments`].
383+ #[ derive( Clone , Copy ) ]
384+ pub ( crate ) enum StaleExpiration {
385+ /// Number of times [`OutboundPayments::remove_stale_payments`] is called.
386+ TimerTicks ( u64 ) ,
387+ /// Duration since the Unix epoch.
388+ AbsoluteTimeout ( core:: time:: Duration ) ,
389+ }
390+
391+ impl_writeable_tlv_based_enum ! ( StaleExpiration ,
392+ ;
393+ ( 0 , TimerTicks ) ,
394+ ( 2 , AbsoluteTimeout )
395+ ) ;
396+
381397/// Indicates an immediate error on [`ChannelManager::send_payment`]. Further errors may be
382398/// surfaced later via [`Event::PaymentPathFailed`] and [`Event::PaymentFailed`].
383399///
@@ -1269,15 +1285,15 @@ impl OutboundPayments {
12691285 }
12701286
12711287 pub ( super ) fn add_new_awaiting_invoice (
1272- & self , payment_id : PaymentId , absolute_expiry : Duration , retry_strategy : Retry ,
1288+ & self , payment_id : PaymentId , expiration : StaleExpiration , retry_strategy : Retry ,
12731289 max_total_routing_fee_msat : Option < u64 >
12741290 ) -> Result < ( ) , ( ) > {
12751291 let mut pending_outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
12761292 match pending_outbounds. entry ( payment_id) {
12771293 hash_map:: Entry :: Occupied ( _) => Err ( ( ) ) ,
12781294 hash_map:: Entry :: Vacant ( entry) => {
12791295 entry. insert ( PendingOutboundPayment :: AwaitingInvoice {
1280- absolute_expiry ,
1296+ expiration ,
12811297 retry_strategy,
12821298 max_total_routing_fee_msat,
12831299 } ) ;
@@ -1547,15 +1563,28 @@ impl OutboundPayments {
15471563 true
15481564 }
15491565 } ,
1550- PendingOutboundPayment :: AwaitingInvoice { absolute_expiry, .. } => {
1551- if duration_since_epoch < * absolute_expiry {
1552- true
1553- } else {
1566+ PendingOutboundPayment :: AwaitingInvoice { expiration, .. } => {
1567+ let is_stale = match expiration {
1568+ StaleExpiration :: AbsoluteTimeout ( absolute_expiry) => {
1569+ * absolute_expiry <= duration_since_epoch
1570+ } ,
1571+ StaleExpiration :: TimerTicks ( timer_ticks_remaining) => {
1572+ if * timer_ticks_remaining > 0 {
1573+ * timer_ticks_remaining -= 1 ;
1574+ false
1575+ } else {
1576+ true
1577+ }
1578+ } ,
1579+ } ;
1580+ if is_stale {
15541581 #[ cfg( invreqfailed) ]
15551582 pending_events. push_back (
15561583 ( events:: Event :: InvoiceRequestFailed { payment_id : * payment_id } , None )
15571584 ) ;
15581585 false
1586+ } else {
1587+ true
15591588 }
15601589 } ,
15611590 _ => true ,
@@ -1775,7 +1804,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
17751804 ( 2 , payment_hash, required) ,
17761805 } ,
17771806 ( 5 , AwaitingInvoice ) => {
1778- ( 0 , absolute_expiry , required) ,
1807+ ( 0 , expiration , required) ,
17791808 ( 2 , retry_strategy, required) ,
17801809 ( 4 , max_total_routing_fee_msat, option) ,
17811810 } ,
@@ -1798,7 +1827,7 @@ mod tests {
17981827 use crate :: ln:: channelmanager:: { PaymentId , RecipientOnionFields } ;
17991828 use crate :: ln:: features:: { ChannelFeatures , NodeFeatures } ;
18001829 use crate :: ln:: msgs:: { ErrorAction , LightningError } ;
1801- use crate :: ln:: outbound_payment:: { Bolt12PaymentError , OutboundPayments , Retry , RetryableSendFailure } ;
1830+ use crate :: ln:: outbound_payment:: { Bolt12PaymentError , OutboundPayments , Retry , RetryableSendFailure , StaleExpiration } ;
18021831 use crate :: offers:: invoice:: DEFAULT_RELATIVE_EXPIRY ;
18031832 use crate :: offers:: offer:: OfferBuilder ;
18041833 use crate :: offers:: test_utils:: * ;
@@ -2004,17 +2033,18 @@ mod tests {
20042033
20052034 #[ test]
20062035 #[ cfg( invreqfailed) ]
2007- fn removes_stale_awaiting_invoice ( ) {
2036+ fn removes_stale_awaiting_invoice_using_absolute_timeout ( ) {
20082037 let pending_events = Mutex :: new ( VecDeque :: new ( ) ) ;
20092038 let outbound_payments = OutboundPayments :: new ( ) ;
20102039 let payment_id = PaymentId ( [ 0 ; 32 ] ) ;
20112040 let absolute_expiry = 100 ;
20122041 let tick_interval = 10 ;
2042+ let expiration = StaleExpiration :: AbsoluteTimeout ( Duration :: from_secs ( absolute_expiry) ) ;
20132043
20142044 assert ! ( !outbound_payments. has_pending_payments( ) ) ;
20152045 assert ! (
20162046 outbound_payments. add_new_awaiting_invoice(
2017- payment_id, Duration :: from_secs ( absolute_expiry ) , Retry :: Attempts ( 0 ) , None
2047+ payment_id, expiration , Retry :: Attempts ( 0 ) , None
20182048 ) . is_ok( )
20192049 ) ;
20202050 assert ! ( outbound_payments. has_pending_payments( ) ) ;
@@ -2040,14 +2070,64 @@ mod tests {
20402070
20412071 assert ! (
20422072 outbound_payments. add_new_awaiting_invoice(
2043- payment_id, Duration :: from_secs ( absolute_expiry + 1 ) , Retry :: Attempts ( 0 ) , None
2073+ payment_id, expiration , Retry :: Attempts ( 0 ) , None
20442074 ) . is_ok( )
20452075 ) ;
20462076 assert ! ( outbound_payments. has_pending_payments( ) ) ;
20472077
20482078 assert ! (
20492079 outbound_payments. add_new_awaiting_invoice(
2050- payment_id, Duration :: from_secs( absolute_expiry + 1 ) , Retry :: Attempts ( 0 ) , None
2080+ payment_id, expiration, Retry :: Attempts ( 0 ) , None
2081+ ) . is_err( )
2082+ ) ;
2083+ }
2084+
2085+ #[ test]
2086+ #[ cfg( invreqfailed) ]
2087+ fn removes_stale_awaiting_invoice_using_timer_ticks ( ) {
2088+ let pending_events = Mutex :: new ( VecDeque :: new ( ) ) ;
2089+ let outbound_payments = OutboundPayments :: new ( ) ;
2090+ let payment_id = PaymentId ( [ 0 ; 32 ] ) ;
2091+ let timer_ticks = 3 ;
2092+ let expiration = StaleExpiration :: TimerTicks ( timer_ticks) ;
2093+
2094+ assert ! ( !outbound_payments. has_pending_payments( ) ) ;
2095+ assert ! (
2096+ outbound_payments. add_new_awaiting_invoice(
2097+ payment_id, expiration, Retry :: Attempts ( 0 ) , None
2098+ ) . is_ok( )
2099+ ) ;
2100+ assert ! ( outbound_payments. has_pending_payments( ) ) ;
2101+
2102+ for i in 0 ..timer_ticks {
2103+ let duration_since_epoch = Duration :: from_secs ( i * 60 ) ;
2104+ outbound_payments. remove_stale_payments ( duration_since_epoch, & pending_events) ;
2105+
2106+ assert ! ( outbound_payments. has_pending_payments( ) ) ;
2107+ assert ! ( pending_events. lock( ) . unwrap( ) . is_empty( ) ) ;
2108+ }
2109+
2110+ let duration_since_epoch = Duration :: from_secs ( timer_ticks * 60 ) ;
2111+ outbound_payments. remove_stale_payments ( duration_since_epoch, & pending_events) ;
2112+
2113+ assert ! ( !outbound_payments. has_pending_payments( ) ) ;
2114+ assert ! ( !pending_events. lock( ) . unwrap( ) . is_empty( ) ) ;
2115+ assert_eq ! (
2116+ pending_events. lock( ) . unwrap( ) . pop_front( ) ,
2117+ Some ( ( Event :: InvoiceRequestFailed { payment_id } , None ) ) ,
2118+ ) ;
2119+ assert ! ( pending_events. lock( ) . unwrap( ) . is_empty( ) ) ;
2120+
2121+ assert ! (
2122+ outbound_payments. add_new_awaiting_invoice(
2123+ payment_id, expiration, Retry :: Attempts ( 0 ) , None
2124+ ) . is_ok( )
2125+ ) ;
2126+ assert ! ( outbound_payments. has_pending_payments( ) ) ;
2127+
2128+ assert ! (
2129+ outbound_payments. add_new_awaiting_invoice(
2130+ payment_id, expiration, Retry :: Attempts ( 0 ) , None
20512131 ) . is_err( )
20522132 ) ;
20532133 }
@@ -2058,12 +2138,12 @@ mod tests {
20582138 let pending_events = Mutex :: new ( VecDeque :: new ( ) ) ;
20592139 let outbound_payments = OutboundPayments :: new ( ) ;
20602140 let payment_id = PaymentId ( [ 0 ; 32 ] ) ;
2061- let absolute_expiry = 100 ;
2141+ let expiration = StaleExpiration :: AbsoluteTimeout ( Duration :: from_secs ( 100 ) ) ;
20622142
20632143 assert ! ( !outbound_payments. has_pending_payments( ) ) ;
20642144 assert ! (
20652145 outbound_payments. add_new_awaiting_invoice(
2066- payment_id, Duration :: from_secs ( absolute_expiry ) , Retry :: Attempts ( 0 ) , None
2146+ payment_id, expiration , Retry :: Attempts ( 0 ) , None
20672147 ) . is_ok( )
20682148 ) ;
20692149 assert ! ( outbound_payments. has_pending_payments( ) ) ;
@@ -2092,11 +2172,11 @@ mod tests {
20922172 let pending_events = Mutex :: new ( VecDeque :: new ( ) ) ;
20932173 let outbound_payments = OutboundPayments :: new ( ) ;
20942174 let payment_id = PaymentId ( [ 0 ; 32 ] ) ;
2095- let absolute_expiry = 100 ;
2175+ let expiration = StaleExpiration :: AbsoluteTimeout ( Duration :: from_secs ( 100 ) ) ;
20962176
20972177 assert ! (
20982178 outbound_payments. add_new_awaiting_invoice(
2099- payment_id, Duration :: from_secs ( absolute_expiry ) , Retry :: Attempts ( 0 ) , None
2179+ payment_id, expiration , Retry :: Attempts ( 0 ) , None
21002180 ) . is_ok( )
21012181 ) ;
21022182 assert ! ( outbound_payments. has_pending_payments( ) ) ;
@@ -2143,7 +2223,7 @@ mod tests {
21432223 let pending_events = Mutex :: new ( VecDeque :: new ( ) ) ;
21442224 let outbound_payments = OutboundPayments :: new ( ) ;
21452225 let payment_id = PaymentId ( [ 0 ; 32 ] ) ;
2146- let absolute_expiry = 100 ;
2226+ let expiration = StaleExpiration :: AbsoluteTimeout ( Duration :: from_secs ( 100 ) ) ;
21472227
21482228 let invoice = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
21492229 . amount_msats ( 1000 )
@@ -2157,7 +2237,7 @@ mod tests {
21572237
21582238 assert ! (
21592239 outbound_payments. add_new_awaiting_invoice(
2160- payment_id, Duration :: from_secs ( absolute_expiry ) , Retry :: Attempts ( 0 ) ,
2240+ payment_id, expiration , Retry :: Attempts ( 0 ) ,
21612241 Some ( invoice. amount_msats( ) / 100 + 50_000 )
21622242 ) . is_ok( )
21632243 ) ;
@@ -2202,7 +2282,7 @@ mod tests {
22022282 let pending_events = Mutex :: new ( VecDeque :: new ( ) ) ;
22032283 let outbound_payments = OutboundPayments :: new ( ) ;
22042284 let payment_id = PaymentId ( [ 0 ; 32 ] ) ;
2205- let absolute_expiry = 100 ;
2285+ let expiration = StaleExpiration :: AbsoluteTimeout ( Duration :: from_secs ( 100 ) ) ;
22062286
22072287 let invoice = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
22082288 . amount_msats ( 1000 )
@@ -2216,7 +2296,7 @@ mod tests {
22162296
22172297 assert ! (
22182298 outbound_payments. add_new_awaiting_invoice(
2219- payment_id, Duration :: from_secs ( absolute_expiry ) , Retry :: Attempts ( 0 ) ,
2299+ payment_id, expiration , Retry :: Attempts ( 0 ) ,
22202300 Some ( invoice. amount_msats( ) / 100 + 50_000 )
22212301 ) . is_ok( )
22222302 ) ;
@@ -2261,7 +2341,7 @@ mod tests {
22612341 let pending_events = Mutex :: new ( VecDeque :: new ( ) ) ;
22622342 let outbound_payments = OutboundPayments :: new ( ) ;
22632343 let payment_id = PaymentId ( [ 0 ; 32 ] ) ;
2264- let absolute_expiry = 100 ;
2344+ let expiration = StaleExpiration :: AbsoluteTimeout ( Duration :: from_secs ( 100 ) ) ;
22652345
22662346 let invoice = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
22672347 . amount_msats ( 1000 )
@@ -2314,7 +2394,7 @@ mod tests {
23142394
23152395 assert ! (
23162396 outbound_payments. add_new_awaiting_invoice(
2317- payment_id, Duration :: from_secs ( absolute_expiry ) , Retry :: Attempts ( 0 ) , Some ( 1234 )
2397+ payment_id, expiration , Retry :: Attempts ( 0 ) , Some ( 1234 )
23182398 ) . is_ok( )
23192399 ) ;
23202400 assert ! ( outbound_payments. has_pending_payments( ) ) ;
0 commit comments