@@ -54,6 +54,15 @@ use crate::sync::Mutex;
5454/// [`ChannelManager::timer_tick_occurred`]: crate::ln::channelmanager::ChannelManager::timer_tick_occurred
5555pub ( crate ) const IDEMPOTENCY_TIMEOUT_TICKS : u8 = 7 ;
5656
57+ #[ cfg( async_payments) ]
58+ /// The default relative expiration to wait for a pending outbound HTLC to a often-offline
59+ /// payee to fulfill.
60+ const ASYNC_PAYMENT_TIMEOUT_RELATIVE_EXPIRY : Duration = Duration :: from_secs ( 60 * 60 * 24 * 7 ) ;
61+
62+ #[ cfg( all( async_payments, test) ) ]
63+ pub ( crate ) const TEST_ASYNC_PAYMENT_TIMEOUT_RELATIVE_EXPIRY : Duration =
64+ ASYNC_PAYMENT_TIMEOUT_RELATIVE_EXPIRY ;
65+
5766/// Stores the session_priv for each part of a payment that is still pending. For versions 0.0.102
5867/// and later, also stores information for retrying the payment.
5968pub ( crate ) enum PendingOutboundPayment {
@@ -98,6 +107,11 @@ pub(crate) enum PendingOutboundPayment {
98107 route_params : RouteParameters ,
99108 invoice_request : InvoiceRequest ,
100109 static_invoice : StaticInvoice ,
110+ // The deadline as duration since the Unix epoch for the async recipient to come online,
111+ // after which we'll fail the payment.
112+ //
113+ // Defaults to [`ASYNC_PAYMENT_TIMEOUT_RELATIVE_EXPIRY`].
114+ expiry_time : Duration ,
101115 } ,
102116 Retryable {
103117 retry_strategy : Option < Retry > ,
@@ -1164,6 +1178,7 @@ impl OutboundPayments {
11641178 abandon_with_entry ! ( entry, PaymentFailureReason :: RouteNotFound ) ;
11651179 return Err ( Bolt12PaymentError :: SendingFailed ( RetryableSendFailure :: OnionPacketSizeExceeded ) )
11661180 }
1181+ let absolute_expiry = duration_since_epoch. saturating_add ( ASYNC_PAYMENT_TIMEOUT_RELATIVE_EXPIRY ) ;
11671182
11681183 * entry. into_mut ( ) = PendingOutboundPayment :: StaticInvoiceReceived {
11691184 payment_hash,
@@ -1176,6 +1191,7 @@ impl OutboundPayments {
11761191 . ok_or ( Bolt12PaymentError :: UnexpectedInvoice ) ?
11771192 . invoice_request ,
11781193 static_invoice : invoice. clone ( ) ,
1194+ expiry_time : absolute_expiry,
11791195 } ;
11801196 return Ok ( ( ) )
11811197 } ,
@@ -2279,11 +2295,12 @@ impl OutboundPayments {
22792295 true
22802296 }
22812297 } ,
2282- PendingOutboundPayment :: StaticInvoiceReceived { route_params, payment_hash, .. } => {
2283- let is_stale =
2298+ PendingOutboundPayment :: StaticInvoiceReceived { route_params, payment_hash, expiry_time, .. } => {
2299+ let is_stale = * expiry_time < duration_since_epoch;
2300+ let is_static_invoice_stale =
22842301 route_params. payment_params . expiry_time . unwrap_or ( u64:: MAX ) <
22852302 duration_since_epoch. as_secs ( ) ;
2286- if is_stale {
2303+ if is_stale || is_static_invoice_stale {
22872304 let fail_ev = events:: Event :: PaymentFailed {
22882305 payment_id : * payment_id,
22892306 payment_hash : Some ( * payment_hash) ,
@@ -2698,6 +2715,9 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
26982715 ( 6 , route_params, required) ,
26992716 ( 8 , invoice_request, required) ,
27002717 ( 10 , static_invoice, required) ,
2718+ // Added in 0.2. Prior versions would have this TLV type defaulted to 0, which is safe because
2719+ // the type is not used.
2720+ ( 11 , expiry_time, ( default_value, Duration :: from_secs( 0 ) ) ) ,
27012721 } ,
27022722 // Added in 0.1. Prior versions will drop these outbounds on downgrade, which is safe because
27032723 // no HTLCs are in-flight.
@@ -3348,6 +3368,7 @@ mod tests {
33483368 route_params,
33493369 invoice_request : dummy_invoice_request ( ) ,
33503370 static_invoice : dummy_static_invoice ( ) ,
3371+ expiry_time : Duration :: from_secs ( absolute_expiry + 2 ) ,
33513372 } ;
33523373 outbounds. insert ( payment_id, outbound) ;
33533374 core:: mem:: drop ( outbounds) ;
@@ -3397,6 +3418,7 @@ mod tests {
33973418 route_params,
33983419 invoice_request : dummy_invoice_request ( ) ,
33993420 static_invoice : dummy_static_invoice ( ) ,
3421+ expiry_time : now ( ) ,
34003422 } ;
34013423 outbounds. insert ( payment_id, outbound) ;
34023424 core:: mem:: drop ( outbounds) ;
0 commit comments