@@ -103,6 +103,10 @@ pub(crate) enum PendingOutboundPayment {
103103 route_params : RouteParameters ,
104104 invoice_request : InvoiceRequest ,
105105 static_invoice : StaticInvoice ,
106+ // Whether we should pay the static invoice asynchronously, i.e. by setting
107+ // [`UpdateAddHTLC::hold_htlc`] so our channel counterparty(s) hold the HTLC(s) for us until the
108+ // recipient comes online, allowing us to go offline after locking in the HTLC(s).
109+ hold_htlcs_at_next_hop : bool ,
106110 // The deadline as duration since the Unix epoch for the async recipient to come online,
107111 // after which we'll fail the payment.
108112 //
@@ -1107,8 +1111,9 @@ impl OutboundPayments {
11071111 }
11081112
11091113 pub ( super ) fn static_invoice_received < ES : Deref > (
1110- & self , invoice : & StaticInvoice , payment_id : PaymentId , features : Bolt12InvoiceFeatures ,
1111- best_block_height : u32 , duration_since_epoch : Duration , entropy_source : ES ,
1114+ & self , invoice : & StaticInvoice , payment_id : PaymentId , hold_htlcs_at_next_hop : bool ,
1115+ features : Bolt12InvoiceFeatures , best_block_height : u32 , duration_since_epoch : Duration ,
1116+ entropy_source : ES ,
11121117 pending_events : & Mutex < VecDeque < ( events:: Event , Option < EventCompletionAction > ) > > ,
11131118 ) -> Result < ( ) , Bolt12PaymentError >
11141119 where
@@ -1192,6 +1197,13 @@ impl OutboundPayments {
11921197 RetryableSendFailure :: OnionPacketSizeExceeded ,
11931198 ) ) ;
11941199 }
1200+
1201+ // If we expect the HTLCs for this payment to be held at our next-hop counterparty, don't
1202+ // retry the payment. In future iterations of this feature, we will send this payment via
1203+ // trampoline and the counterparty will retry on our behalf.
1204+ if hold_htlcs_at_next_hop {
1205+ * retry_strategy = Retry :: Attempts ( 0 ) ;
1206+ }
11951207 let absolute_expiry =
11961208 duration_since_epoch. saturating_add ( ASYNC_PAYMENT_TIMEOUT_RELATIVE_EXPIRY ) ;
11971209
@@ -1200,6 +1212,7 @@ impl OutboundPayments {
12001212 keysend_preimage,
12011213 retry_strategy : * retry_strategy,
12021214 route_params,
1215+ hold_htlcs_at_next_hop,
12031216 invoice_request : retryable_invoice_request
12041217 . take ( )
12051218 . ok_or ( Bolt12PaymentError :: UnexpectedInvoice ) ?
@@ -2759,6 +2772,12 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
27592772 // HTLCs are in-flight.
27602773 ( 9 , StaticInvoiceReceived ) => {
27612774 ( 0 , payment_hash, required) ,
2775+ // Added in 0.2. If this field is set when this variant is created, the HTLCs are sent
2776+ // immediately after and the pending outbound is also immediately transitioned to Retryable.
2777+ // However, if we crash and then downgrade before the transition to Retryable, this payment will
2778+ // sit in outbounds until it either times out in `remove_stale_payments` or is manually
2779+ // abandoned.
2780+ ( 1 , hold_htlcs_at_next_hop, required) ,
27622781 ( 2 , keysend_preimage, required) ,
27632782 ( 4 , retry_strategy, required) ,
27642783 ( 6 , route_params, required) ,
@@ -3418,6 +3437,7 @@ mod tests {
34183437 invoice_request : dummy_invoice_request ( ) ,
34193438 static_invoice : dummy_static_invoice ( ) ,
34203439 expiry_time : Duration :: from_secs ( absolute_expiry + 2 ) ,
3440+ hold_htlcs_at_next_hop : false
34213441 } ;
34223442 outbounds. insert ( payment_id, outbound) ;
34233443 core:: mem:: drop ( outbounds) ;
@@ -3468,6 +3488,7 @@ mod tests {
34683488 invoice_request : dummy_invoice_request ( ) ,
34693489 static_invoice : dummy_static_invoice ( ) ,
34703490 expiry_time : now ( ) ,
3491+ hold_htlcs_at_next_hop : false ,
34713492 } ;
34723493 outbounds. insert ( payment_id, outbound) ;
34733494 core:: mem:: drop ( outbounds) ;
0 commit comments