@@ -228,7 +228,7 @@ where
228
228
if invoice. amount_milli_satoshis ( ) . is_none ( ) {
229
229
Err ( PaymentError :: Invoice ( "amount missing" ) )
230
230
} else {
231
- self . pay_invoice_internal ( invoice, None )
231
+ self . pay_invoice_internal ( invoice, None , 0 )
232
232
}
233
233
}
234
234
@@ -244,59 +244,142 @@ where
244
244
if invoice. amount_milli_satoshis ( ) . is_some ( ) {
245
245
Err ( PaymentError :: Invoice ( "amount unexpected" ) )
246
246
} else {
247
- self . pay_invoice_internal ( invoice, Some ( amount_msats) )
247
+ self . pay_invoice_internal ( invoice, Some ( amount_msats) , 0 )
248
248
}
249
249
}
250
250
251
251
fn pay_invoice_internal (
252
- & self , invoice : & Invoice , amount_msats : Option < u64 >
252
+ & self , invoice : & Invoice , amount_msats : Option < u64 > , retry_count : usize
253
253
) -> Result < PaymentId , PaymentError > {
254
254
debug_assert ! ( invoice. amount_milli_satoshis( ) . is_some( ) ^ amount_msats. is_some( ) ) ;
255
255
let payment_hash = PaymentHash ( invoice. payment_hash ( ) . clone ( ) . into_inner ( ) ) ;
256
- let mut payment_cache = self . payment_cache . lock ( ) . unwrap ( ) ;
257
- match payment_cache. entry ( payment_hash) {
258
- hash_map:: Entry :: Vacant ( entry) => {
259
- let payer = self . payer . node_id ( ) ;
260
- let mut payee = Payee :: new ( invoice. recover_payee_pub_key ( ) )
261
- . with_expiry_time ( expiry_time_from_unix_epoch ( & invoice) . as_secs ( ) )
262
- . with_route_hints ( invoice. route_hints ( ) ) ;
263
- if let Some ( features) = invoice. features ( ) {
264
- payee = payee. with_features ( features. clone ( ) ) ;
265
- }
266
- let params = RouteParameters {
267
- payee,
268
- final_value_msat : invoice. amount_milli_satoshis ( ) . or ( amount_msats) . unwrap ( ) ,
269
- final_cltv_expiry_delta : invoice. min_final_cltv_expiry ( ) as u32 ,
256
+ let failed_paths_data = loop {
257
+ let mut payment_cache = self . payment_cache . lock ( ) . unwrap ( ) ;
258
+ match payment_cache. entry ( payment_hash) {
259
+ hash_map:: Entry :: Vacant ( entry) => {
260
+ let payer = self . payer . node_id ( ) ;
261
+ let mut payee = Payee :: new ( invoice. recover_payee_pub_key ( ) )
262
+ . with_expiry_time ( expiry_time_from_unix_epoch ( & invoice) . as_secs ( ) )
263
+ . with_route_hints ( invoice. route_hints ( ) ) ;
264
+ if let Some ( features) = invoice. features ( ) {
265
+ payee = payee. with_features ( features. clone ( ) ) ;
266
+ }
267
+ let params = RouteParameters {
268
+ payee,
269
+ final_value_msat : invoice. amount_milli_satoshis ( ) . or ( amount_msats) . unwrap ( ) ,
270
+ final_cltv_expiry_delta : invoice. min_final_cltv_expiry ( ) as u32 ,
271
+ } ;
272
+ let first_hops = self . payer . first_hops ( ) ;
273
+ let route = self . router . find_route (
274
+ & payer,
275
+ & params,
276
+ Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) ) ,
277
+ & self . scorer . lock ( ) ,
278
+ ) . map_err ( |e| PaymentError :: Routing ( e) ) ?;
279
+
280
+ let payment_secret = Some ( invoice. payment_secret ( ) . clone ( ) ) ;
281
+ let payment_id = match self . payer . send_payment ( & route, payment_hash, & payment_secret) {
282
+ Ok ( payment_id) => payment_id,
283
+ Err ( PaymentSendFailure :: ParameterError ( e) ) =>
284
+ return Err ( PaymentError :: Sending ( PaymentSendFailure :: ParameterError ( e) ) ) ,
285
+ Err ( PaymentSendFailure :: PathParameterError ( e) ) =>
286
+ return Err ( PaymentError :: Sending ( PaymentSendFailure :: PathParameterError ( e) ) ) ,
287
+ Err ( PaymentSendFailure :: AllFailedRetrySafe ( e) ) => {
288
+ if retry_count >= self . retry_attempts . 0 {
289
+ return Err ( PaymentError :: Sending ( PaymentSendFailure :: AllFailedRetrySafe ( e) ) )
290
+ }
291
+ break None ;
292
+ } ,
293
+ Err ( PaymentSendFailure :: PartialFailure { results : _, failed_paths_retry, payment_id } ) => {
294
+ if let Some ( retry_data) = failed_paths_retry {
295
+ entry. insert ( retry_count) ;
296
+ break Some ( ( retry_data, payment_id) ) ;
297
+ } else {
298
+ // This may happen if we send a payment and some paths fail, but
299
+ // only due to a temporary monitor failure or the like, implying
300
+ // they're really in-flight, but we haven't sent the initial
301
+ // HTLC-Add messages yet.
302
+ payment_id
303
+ }
304
+ } ,
305
+ } ;
306
+ entry. insert ( retry_count) ;
307
+ return Ok ( payment_id) ;
308
+ } ,
309
+ hash_map:: Entry :: Occupied ( _) => return Err ( PaymentError :: Invoice ( "payment pending" ) ) ,
310
+ }
311
+ } ;
312
+ if let Some ( ( retry, payment_id) ) = failed_paths_data {
313
+ // Some paths were sent, even if we failed to send the full MPP value our recipient may
314
+ // misbehave and claim the funds, at which point we have to consider the payment sent,
315
+ // so return `Ok()` here, ignoring any retry errors.
316
+ let _ = self . retry_payment ( true , payment_id, payment_hash, & retry) ;
317
+ Ok ( payment_id)
318
+ } else {
319
+ self . pay_invoice_internal ( invoice, amount_msats, retry_count + 1 )
320
+ }
321
+ }
322
+
323
+ fn retry_payment ( & self , other_paths_pending : bool , payment_id : PaymentId , payment_hash : PaymentHash , params : & RouteParameters )
324
+ -> Result < ( ) , ( ) > {
325
+ let route;
326
+ {
327
+ let mut payment_cache = self . payment_cache . lock ( ) . unwrap ( ) ;
328
+ let entry = loop {
329
+ let entry = payment_cache. entry ( payment_hash) ;
330
+ match entry {
331
+ hash_map:: Entry :: Occupied ( _) => break entry,
332
+ hash_map:: Entry :: Vacant ( entry) => entry. insert ( 0 ) ,
270
333
} ;
334
+ } ;
335
+ if let hash_map:: Entry :: Occupied ( mut entry) = entry {
336
+ let max_payment_attempts = self . retry_attempts . 0 + 1 ;
337
+ let attempts = entry. get_mut ( ) ;
338
+ * attempts += 1 ;
339
+
340
+ if * attempts >= max_payment_attempts {
341
+ log_trace ! ( self . logger, "Payment {} exceeded maximum attempts; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
342
+ if !other_paths_pending { entry. remove ( ) ; }
343
+ return Err ( ( ) ) ;
344
+ } else if has_expired ( params) {
345
+ log_trace ! ( self . logger, "Invoice expired for payment {}; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
346
+ if !other_paths_pending { entry. remove ( ) ; }
347
+ return Err ( ( ) ) ;
348
+ }
349
+
350
+ let payer = self . payer . node_id ( ) ;
271
351
let first_hops = self . payer . first_hops ( ) ;
272
- let route = self . router . find_route (
273
- & payer,
274
- & params,
275
- Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) ) ,
276
- & self . scorer . lock ( ) ,
277
- ) . map_err ( |e| PaymentError :: Routing ( e) ) ?;
278
-
279
- let payment_hash = PaymentHash ( invoice. payment_hash ( ) . clone ( ) . into_inner ( ) ) ;
280
- let payment_secret = Some ( invoice. payment_secret ( ) . clone ( ) ) ;
281
- let payment_id = self . payer . send_payment ( & route, payment_hash, & payment_secret)
282
- . map_err ( |e| PaymentError :: Sending ( e) ) ?;
283
- entry. insert ( 0 ) ;
284
- Ok ( payment_id)
285
- } ,
286
- hash_map:: Entry :: Occupied ( _) => Err ( PaymentError :: Invoice ( "payment pending" ) ) ,
352
+ route = self . router . find_route ( & payer, & params, Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) ) , & self . scorer . lock ( ) ) ;
353
+ if route. is_err ( ) {
354
+ log_trace ! ( self . logger, "Failed to find a route for payment {}; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
355
+ if !other_paths_pending { entry. remove ( ) ; }
356
+ return Err ( ( ) ) ;
357
+ }
358
+ } else {
359
+ unreachable ! ( ) ;
360
+ }
287
361
}
288
- }
289
362
290
- fn retry_payment (
291
- & self , payment_id : PaymentId , params : & RouteParameters
292
- ) -> Result < ( ) , PaymentError > {
293
- let payer = self . payer . node_id ( ) ;
294
- let first_hops = self . payer . first_hops ( ) ;
295
- let route = self . router . find_route (
296
- & payer, & params, Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) ) ,
297
- & self . scorer . lock ( )
298
- ) . map_err ( |e| PaymentError :: Routing ( e) ) ?;
299
- self . payer . retry_payment ( & route, payment_id) . map_err ( |e| PaymentError :: Sending ( e) )
363
+ let retry_res = self . payer . retry_payment ( & route. unwrap ( ) , payment_id) ;
364
+ match retry_res {
365
+ Ok ( ( ) ) => Ok ( ( ) ) ,
366
+ Err ( PaymentSendFailure :: ParameterError ( _) ) |
367
+ Err ( PaymentSendFailure :: PathParameterError ( _) ) => {
368
+ log_trace ! ( self . logger, "Failed to retry for payment {} due to bogus route/payment data, not retrying." , log_bytes!( payment_hash. 0 ) ) ;
369
+ if !other_paths_pending { self . payment_cache . lock ( ) . unwrap ( ) . remove ( & payment_hash) ; }
370
+ return Err ( ( ) ) ;
371
+ } ,
372
+ Err ( PaymentSendFailure :: AllFailedRetrySafe ( _) ) => {
373
+ self . retry_payment ( other_paths_pending, payment_id, payment_hash, params)
374
+ } ,
375
+ Err ( PaymentSendFailure :: PartialFailure { results : _, failed_paths_retry, .. } ) => {
376
+ if let Some ( retry) = failed_paths_retry {
377
+ self . retry_payment ( true , payment_id, payment_hash, & retry)
378
+ } else {
379
+ Ok ( ( ) )
380
+ }
381
+ } ,
382
+ }
300
383
}
301
384
302
385
/// Removes the payment cached by the given payment hash.
@@ -328,48 +411,25 @@ where
328
411
{
329
412
fn handle_event ( & self , event : & Event ) {
330
413
match event {
331
- Event :: PaymentPathFailed {
332
- payment_id, payment_hash, rejected_by_dest, path, short_channel_id, retry, ..
333
- } => {
414
+ Event :: PaymentPathFailed { all_paths_failed, payment_id, payment_hash, rejected_by_dest, path, short_channel_id, retry, .. } => {
334
415
if let Some ( short_channel_id) = short_channel_id {
335
416
self . scorer . lock ( ) . payment_path_failed ( path, * short_channel_id) ;
336
417
}
337
418
338
- let mut payment_cache = self . payment_cache . lock ( ) . unwrap ( ) ;
339
- let entry = loop {
340
- let entry = payment_cache. entry ( * payment_hash) ;
341
- match entry {
342
- hash_map:: Entry :: Occupied ( _) => break entry,
343
- hash_map:: Entry :: Vacant ( entry) => entry. insert ( 0 ) ,
344
- } ;
345
- } ;
346
- if let hash_map:: Entry :: Occupied ( mut entry) = entry {
347
- let max_payment_attempts = self . retry_attempts . 0 + 1 ;
348
- let attempts = entry. get_mut ( ) ;
349
- * attempts += 1 ;
350
-
351
- if * rejected_by_dest {
352
- log_trace ! ( self . logger, "Payment {} rejected by destination; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
353
- } else if payment_id. is_none ( ) {
354
- log_trace ! ( self . logger, "Payment {} has no id; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
355
- } else if * attempts >= max_payment_attempts {
356
- log_trace ! ( self . logger, "Payment {} exceeded maximum attempts; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
357
- } else if retry. is_none ( ) {
358
- log_trace ! ( self . logger, "Payment {} missing retry params; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
359
- } else if has_expired ( retry. as_ref ( ) . unwrap ( ) ) {
360
- log_trace ! ( self . logger, "Invoice expired for payment {}; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
361
- } else if self . retry_payment ( * payment_id. as_ref ( ) . unwrap ( ) , retry. as_ref ( ) . unwrap ( ) ) . is_err ( ) {
362
- log_trace ! ( self . logger, "Error retrying payment {}; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
363
- } else {
364
- log_trace ! ( self . logger, "Payment {} failed; retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
419
+ if * rejected_by_dest {
420
+ log_trace ! ( self . logger, "Payment {} rejected by destination; not retrying" , log_bytes!( payment_hash. 0 ) ) ;
421
+ if * all_paths_failed { self . payment_cache . lock ( ) . unwrap ( ) . remove ( payment_hash) ; }
422
+ } else if payment_id. is_none ( ) {
423
+ log_trace ! ( self . logger, "Payment {} has no id; not retrying" , log_bytes!( payment_hash. 0 ) ) ;
424
+ if * all_paths_failed { self . payment_cache . lock ( ) . unwrap ( ) . remove ( payment_hash) ; }
425
+ } else if let Some ( params) = retry {
426
+ if self . retry_payment ( !all_paths_failed, payment_id. unwrap ( ) , * payment_hash, params) . is_ok ( ) {
427
+ // We retried at least somewhat, don't provide the PaymentPathFailed event to the user.
365
428
return ;
366
429
}
367
-
368
- // Either the payment was rejected, the maximum attempts were exceeded, or an
369
- // error occurred when attempting to retry.
370
- entry. remove ( ) ;
371
430
} else {
372
- unreachable ! ( ) ;
431
+ log_trace ! ( self . logger, "Payment {} missing retry params; not retrying" , log_bytes!( payment_hash. 0 ) ) ;
432
+ if * all_paths_failed { self . payment_cache . lock ( ) . unwrap ( ) . remove ( payment_hash) ; }
373
433
}
374
434
} ,
375
435
Event :: PaymentSent { payment_hash, .. } => {
0 commit comments