@@ -103,6 +103,10 @@ pub(crate) enum PendingOutboundPayment {
103
103
route_params : RouteParameters ,
104
104
invoice_request : InvoiceRequest ,
105
105
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 ,
106
110
// The deadline as duration since the Unix epoch for the async recipient to come online,
107
111
// after which we'll fail the payment.
108
112
//
@@ -1107,8 +1111,9 @@ impl OutboundPayments {
1107
1111
}
1108
1112
1109
1113
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 ,
1112
1117
pending_events : & Mutex < VecDeque < ( events:: Event , Option < EventCompletionAction > ) > > ,
1113
1118
) -> Result < ( ) , Bolt12PaymentError >
1114
1119
where
@@ -1192,6 +1197,13 @@ impl OutboundPayments {
1192
1197
RetryableSendFailure :: OnionPacketSizeExceeded ,
1193
1198
) ) ;
1194
1199
}
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
+ }
1195
1207
let absolute_expiry =
1196
1208
duration_since_epoch. saturating_add ( ASYNC_PAYMENT_TIMEOUT_RELATIVE_EXPIRY ) ;
1197
1209
@@ -1200,6 +1212,7 @@ impl OutboundPayments {
1200
1212
keysend_preimage,
1201
1213
retry_strategy : * retry_strategy,
1202
1214
route_params,
1215
+ hold_htlcs_at_next_hop,
1203
1216
invoice_request : retryable_invoice_request
1204
1217
. take ( )
1205
1218
. ok_or ( Bolt12PaymentError :: UnexpectedInvoice ) ?
@@ -2759,6 +2772,12 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
2759
2772
// HTLCs are in-flight.
2760
2773
( 9 , StaticInvoiceReceived ) => {
2761
2774
( 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) ,
2762
2781
( 2 , keysend_preimage, required) ,
2763
2782
( 4 , retry_strategy, required) ,
2764
2783
( 6 , route_params, required) ,
@@ -3418,6 +3437,7 @@ mod tests {
3418
3437
invoice_request : dummy_invoice_request ( ) ,
3419
3438
static_invoice : dummy_static_invoice ( ) ,
3420
3439
expiry_time : Duration :: from_secs ( absolute_expiry + 2 ) ,
3440
+ hold_htlcs_at_next_hop : false
3421
3441
} ;
3422
3442
outbounds. insert ( payment_id, outbound) ;
3423
3443
core:: mem:: drop ( outbounds) ;
@@ -3468,6 +3488,7 @@ mod tests {
3468
3488
invoice_request : dummy_invoice_request ( ) ,
3469
3489
static_invoice : dummy_static_invoice ( ) ,
3470
3490
expiry_time : now ( ) ,
3491
+ hold_htlcs_at_next_hop : false ,
3471
3492
} ;
3472
3493
outbounds. insert ( payment_id, outbound) ;
3473
3494
core:: mem:: drop ( outbounds) ;
0 commit comments