@@ -201,7 +201,7 @@ where
201
201
if invoice. amount_milli_satoshis ( ) . is_none ( ) {
202
202
Err ( PaymentError :: Invoice ( "amount missing" ) )
203
203
} else {
204
- self . pay_invoice_internal ( invoice, None )
204
+ self . pay_invoice_internal ( invoice, None , 0 )
205
205
}
206
206
}
207
207
@@ -213,57 +213,142 @@ where
213
213
if invoice. amount_milli_satoshis ( ) . is_some ( ) {
214
214
Err ( PaymentError :: Invoice ( "amount unexpected" ) )
215
215
} else {
216
- self . pay_invoice_internal ( invoice, Some ( amount_msats) )
216
+ self . pay_invoice_internal ( invoice, Some ( amount_msats) , 0 )
217
217
}
218
218
}
219
219
220
220
fn pay_invoice_internal (
221
- & self , invoice : & Invoice , amount_msats : Option < u64 >
221
+ & self , invoice : & Invoice , amount_msats : Option < u64 > , retry_count : usize
222
222
) -> Result < PaymentId , PaymentError > {
223
223
debug_assert ! ( invoice. amount_milli_satoshis( ) . is_some( ) ^ amount_msats. is_some( ) ) ;
224
224
let payment_hash = PaymentHash ( invoice. payment_hash ( ) . clone ( ) . into_inner ( ) ) ;
225
- let mut payment_cache = self . payment_cache . lock ( ) . unwrap ( ) ;
226
- match payment_cache. entry ( payment_hash) {
227
- hash_map:: Entry :: Vacant ( entry) => {
228
- let payer = self . payer . node_id ( ) ;
229
- let mut payee = Payee :: new ( invoice. recover_payee_pub_key ( ) )
230
- . with_expiry_time ( expiry_time_from_unix_epoch ( & invoice) . as_secs ( ) )
231
- . with_route_hints ( invoice. route_hints ( ) ) ;
232
- if let Some ( features) = invoice. features ( ) {
233
- payee = payee. with_features ( features. clone ( ) ) ;
234
- }
235
- let params = RouteParameters {
236
- payee,
237
- final_value_msat : invoice. amount_milli_satoshis ( ) . or ( amount_msats) . unwrap ( ) ,
238
- final_cltv_expiry_delta : invoice. min_final_cltv_expiry ( ) as u32 ,
225
+ let failed_paths_data = loop {
226
+ let payment_hash = PaymentHash ( invoice. payment_hash ( ) . clone ( ) . into_inner ( ) ) ;
227
+ let mut payment_cache = self . payment_cache . lock ( ) . unwrap ( ) ;
228
+ match payment_cache. entry ( payment_hash) {
229
+ hash_map:: Entry :: Vacant ( entry) => {
230
+ let payer = self . payer . node_id ( ) ;
231
+ let mut payee = Payee :: new ( invoice. recover_payee_pub_key ( ) )
232
+ . with_expiry_time ( expiry_time_from_unix_epoch ( & invoice) . as_secs ( ) )
233
+ . with_route_hints ( invoice. route_hints ( ) ) ;
234
+ if let Some ( features) = invoice. features ( ) {
235
+ payee = payee. with_features ( features. clone ( ) ) ;
236
+ }
237
+ let params = RouteParameters {
238
+ payee,
239
+ final_value_msat : invoice. amount_milli_satoshis ( ) . or ( amount_msats) . unwrap ( ) ,
240
+ final_cltv_expiry_delta : invoice. min_final_cltv_expiry ( ) as u32 ,
241
+ } ;
242
+ let first_hops = self . payer . first_hops ( ) ;
243
+ let route = self . router . find_route (
244
+ & payer,
245
+ & params,
246
+ Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) ) ,
247
+ ) . map_err ( |e| PaymentError :: Routing ( e) ) ?;
248
+
249
+ let payment_secret = Some ( invoice. payment_secret ( ) . clone ( ) ) ;
250
+ let payment_id = match self . payer . send_payment ( & route, payment_hash, & payment_secret) {
251
+ Ok ( payment_id) => payment_id,
252
+ Err ( PaymentSendFailure :: ParameterError ( e) ) =>
253
+ Err ( PaymentError :: Sending ( PaymentSendFailure :: ParameterError ( e) ) ) ?,
254
+ Err ( PaymentSendFailure :: PathParameterError ( e) ) =>
255
+ Err ( PaymentError :: Sending ( PaymentSendFailure :: PathParameterError ( e) ) ) ?,
256
+ Err ( PaymentSendFailure :: AllFailedRetrySafe ( e) ) => {
257
+ if retry_count >= self . retry_attempts . 0 {
258
+ Err ( PaymentError :: Sending ( PaymentSendFailure :: AllFailedRetrySafe ( e) ) ) ?
259
+ }
260
+ break None ;
261
+ } ,
262
+ Err ( PaymentSendFailure :: PartialFailure { results : _, failed_paths_retry, payment_id } ) => {
263
+ if let Some ( retry_data) = failed_paths_retry {
264
+ entry. insert ( retry_count) ;
265
+ break Some ( ( retry_data, payment_id) ) ;
266
+ } else {
267
+ // This may happen if we send a payment and some paths fail, but
268
+ // only due to a temporary monitor failure or the like, implying
269
+ // they're really in-flight, but we haven't sent the initial
270
+ // HTLC-Add messages yet.
271
+ payment_id
272
+ }
273
+ } ,
274
+ } ;
275
+ entry. insert ( retry_count) ;
276
+ return Ok ( payment_id) ;
277
+ } ,
278
+ hash_map:: Entry :: Occupied ( _) => Err ( PaymentError :: Invoice ( "payment pending" ) ) ?,
279
+ }
280
+ } ;
281
+ if let Some ( ( retry, payment_id) ) = failed_paths_data {
282
+ // Some paths were sent, even if we failed to send the full MPP value our recipient may
283
+ // misbehave and claim the funds, at which point we have to consider the payment sent,
284
+ // so return `Ok()` here, ignoring any retry errors.
285
+ let _ = self . retry_payment ( true , payment_id, payment_hash, & retry) ;
286
+ Ok ( payment_id)
287
+ } else {
288
+ self . pay_invoice_internal ( invoice, amount_msats, retry_count + 1 )
289
+ }
290
+ }
291
+
292
+ fn retry_payment ( & self , other_paths_pending : bool , payment_id : PaymentId , payment_hash : PaymentHash , params : & RouteParameters )
293
+ -> Result < ( ) , ( ) > {
294
+ let route;
295
+ {
296
+ let mut payment_cache = self . payment_cache . lock ( ) . unwrap ( ) ;
297
+ let entry = loop {
298
+ let entry = payment_cache. entry ( payment_hash) ;
299
+ match entry {
300
+ hash_map:: Entry :: Occupied ( _) => break entry,
301
+ hash_map:: Entry :: Vacant ( entry) => entry. insert ( 0 ) ,
239
302
} ;
303
+ } ;
304
+ if let hash_map:: Entry :: Occupied ( mut entry) = entry {
305
+ let max_payment_attempts = self . retry_attempts . 0 + 1 ;
306
+ let attempts = entry. get_mut ( ) ;
307
+ * attempts += 1 ;
308
+
309
+ if * attempts >= max_payment_attempts {
310
+ log_trace ! ( self . logger, "Payment {} exceeded maximum attempts; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
311
+ if !other_paths_pending { entry. remove ( ) ; }
312
+ return Err ( ( ) ) ;
313
+ } else if has_expired ( params) {
314
+ log_trace ! ( self . logger, "Invoice expired for payment {}; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
315
+ if !other_paths_pending { entry. remove ( ) ; }
316
+ return Err ( ( ) ) ;
317
+ }
318
+
319
+ let payer = self . payer . node_id ( ) ;
240
320
let first_hops = self . payer . first_hops ( ) ;
241
- let route = self . router . find_route (
242
- & payer,
243
- & params,
244
- Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) ) ,
245
- ) . map_err ( |e| PaymentError :: Routing ( e) ) ?;
246
-
247
- let payment_hash = PaymentHash ( invoice. payment_hash ( ) . clone ( ) . into_inner ( ) ) ;
248
- let payment_secret = Some ( invoice. payment_secret ( ) . clone ( ) ) ;
249
- let payment_id = self . payer . send_payment ( & route, payment_hash, & payment_secret)
250
- . map_err ( |e| PaymentError :: Sending ( e) ) ?;
251
- entry. insert ( 0 ) ;
252
- Ok ( payment_id)
253
- } ,
254
- hash_map:: Entry :: Occupied ( _) => Err ( PaymentError :: Invoice ( "payment pending" ) ) ,
321
+ route = self . router . find_route ( & payer, & params, Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) ) ) ;
322
+ if route. is_err ( ) {
323
+ log_trace ! ( self . logger, "Failed to find a route for payment {}; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
324
+ if !other_paths_pending { entry. remove ( ) ; }
325
+ return Err ( ( ) ) ;
326
+ }
327
+ } else {
328
+ unreachable ! ( ) ;
329
+ }
255
330
}
256
- }
257
331
258
- fn retry_payment (
259
- & self , payment_id : PaymentId , params : & RouteParameters
260
- ) -> Result < ( ) , PaymentError > {
261
- let payer = self . payer . node_id ( ) ;
262
- let first_hops = self . payer . first_hops ( ) ;
263
- let route = self . router . find_route (
264
- & payer, & params, Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) )
265
- ) . map_err ( |e| PaymentError :: Routing ( e) ) ?;
266
- self . payer . retry_payment ( & route, payment_id) . map_err ( |e| PaymentError :: Sending ( e) )
332
+ let retry_res = self . payer . retry_payment ( & route. unwrap ( ) , payment_id) ;
333
+ match retry_res {
334
+ Ok ( ( ) ) => Ok ( ( ) ) ,
335
+ Err ( PaymentSendFailure :: ParameterError ( _) ) |
336
+ Err ( PaymentSendFailure :: PathParameterError ( _) ) => {
337
+ log_trace ! ( self . logger, "Failed to retry for payment {} due to bogus route/payment data, not retrying." , log_bytes!( payment_hash. 0 ) ) ;
338
+ if !other_paths_pending { self . payment_cache . lock ( ) . unwrap ( ) . remove ( & payment_hash) ; }
339
+ return Err ( ( ) ) ;
340
+ } ,
341
+ Err ( PaymentSendFailure :: AllFailedRetrySafe ( _) ) => {
342
+ self . retry_payment ( other_paths_pending, payment_id, payment_hash, params)
343
+ } ,
344
+ Err ( PaymentSendFailure :: PartialFailure { results : _, failed_paths_retry, .. } ) => {
345
+ if let Some ( retry) = failed_paths_retry {
346
+ self . retry_payment ( true , payment_id, payment_hash, & retry)
347
+ } else {
348
+ Ok ( ( ) )
349
+ }
350
+ } ,
351
+ }
267
352
}
268
353
269
354
/// Removes the payment cached by the given payment hash.
@@ -293,42 +378,21 @@ where
293
378
{
294
379
fn handle_event ( & self , event : & Event ) {
295
380
match event {
296
- Event :: PaymentPathFailed { payment_id, payment_hash, rejected_by_dest, retry, .. } => {
297
- let mut payment_cache = self . payment_cache . lock ( ) . unwrap ( ) ;
298
- let entry = loop {
299
- let entry = payment_cache. entry ( * payment_hash) ;
300
- match entry {
301
- hash_map:: Entry :: Occupied ( _) => break entry,
302
- hash_map:: Entry :: Vacant ( entry) => entry. insert ( 0 ) ,
303
- } ;
304
- } ;
305
- if let hash_map:: Entry :: Occupied ( mut entry) = entry {
306
- let max_payment_attempts = self . retry_attempts . 0 + 1 ;
307
- let attempts = entry. get_mut ( ) ;
308
- * attempts += 1 ;
309
-
310
- if * rejected_by_dest {
311
- log_trace ! ( self . logger, "Payment {} rejected by destination; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
312
- } else if payment_id. is_none ( ) {
313
- log_trace ! ( self . logger, "Payment {} has no id; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
314
- } else if * attempts >= max_payment_attempts {
315
- log_trace ! ( self . logger, "Payment {} exceeded maximum attempts; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
316
- } else if retry. is_none ( ) {
317
- log_trace ! ( self . logger, "Payment {} missing retry params; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
318
- } else if has_expired ( retry. as_ref ( ) . unwrap ( ) ) {
319
- log_trace ! ( self . logger, "Invoice expired for payment {}; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
320
- } else if self . retry_payment ( * payment_id. as_ref ( ) . unwrap ( ) , retry. as_ref ( ) . unwrap ( ) ) . is_err ( ) {
321
- log_trace ! ( self . logger, "Error retrying payment {}; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
322
- } else {
323
- log_trace ! ( self . logger, "Payment {} failed; retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
381
+ Event :: PaymentPathFailed { all_paths_failed, payment_id, payment_hash, rejected_by_dest, retry, .. } => {
382
+ if * rejected_by_dest {
383
+ log_trace ! ( self . logger, "Payment {} rejected by destination; not retrying" , log_bytes!( payment_hash. 0 ) ) ;
384
+ if * all_paths_failed { self . payment_cache . lock ( ) . unwrap ( ) . remove ( payment_hash) ; }
385
+ } else if payment_id. is_none ( ) {
386
+ log_trace ! ( self . logger, "Payment {} has no id; not retrying" , log_bytes!( payment_hash. 0 ) ) ;
387
+ if * all_paths_failed { self . payment_cache . lock ( ) . unwrap ( ) . remove ( payment_hash) ; }
388
+ } else if let Some ( params) = retry {
389
+ if self . retry_payment ( !all_paths_failed, payment_id. unwrap ( ) , * payment_hash, params) . is_ok ( ) {
390
+ // We retried at least somewhat, don't provide the PaymentPathFailed event to the user.
324
391
return ;
325
392
}
326
-
327
- // Either the payment was rejected, the maximum attempts were exceeded, or an
328
- // error occurred when attempting to retry.
329
- entry. remove ( ) ;
330
393
} else {
331
- unreachable ! ( ) ;
394
+ log_trace ! ( self . logger, "Payment {} missing retry params; not retrying" , log_bytes!( payment_hash. 0 ) ) ;
395
+ if * all_paths_failed { self . payment_cache . lock ( ) . unwrap ( ) . remove ( payment_hash) ; }
332
396
}
333
397
} ,
334
398
Event :: PaymentSent { payment_hash, .. } => {
0 commit comments