@@ -15,10 +15,10 @@ use bitcoin::secp256k1::{self, Secp256k1, SecretKey};
15
15
16
16
use crate :: chain:: keysinterface:: { EntropySource , NodeSigner , Recipient } ;
17
17
use crate :: ln:: { PaymentHash , PaymentPreimage , PaymentSecret } ;
18
- use crate :: ln:: channelmanager:: { HTLCSource , IDEMPOTENCY_TIMEOUT_TICKS , MIN_HTLC_RELAY_HOLDING_CELL_MILLIS , PaymentId } ;
18
+ use crate :: ln:: channelmanager:: { ChannelDetails , HTLCSource , IDEMPOTENCY_TIMEOUT_TICKS , MIN_HTLC_RELAY_HOLDING_CELL_MILLIS , PaymentId } ;
19
19
use crate :: ln:: msgs:: DecodeError ;
20
20
use crate :: ln:: onion_utils:: HTLCFailReason ;
21
- use crate :: routing:: router:: { PaymentParameters , Route , RouteHop , RouteParameters , RoutePath } ;
21
+ use crate :: routing:: router:: { InFlightHtlcs , PaymentParameters , Route , RouteHop , RouteParameters , RoutePath , Router } ;
22
22
use crate :: util:: errors:: APIError ;
23
23
use crate :: util:: events;
24
24
use crate :: util:: logger:: Logger ;
@@ -237,6 +237,16 @@ impl Retry {
237
237
}
238
238
}
239
239
240
+ #[ cfg( feature = "std" ) ]
241
+ pub ( super ) fn has_expired ( route_params : & RouteParameters ) -> bool {
242
+ if let Some ( expiry_time) = route_params. payment_params . expiry_time {
243
+ if let Ok ( elapsed) = std:: time:: SystemTime :: UNIX_EPOCH . elapsed ( ) {
244
+ return elapsed > core:: time:: Duration :: from_secs ( expiry_time)
245
+ }
246
+ }
247
+ false
248
+ }
249
+
240
250
pub ( crate ) type PaymentAttempts = PaymentAttemptsUsingTime < ConfiguredTime > ;
241
251
242
252
/// Storing minimal payment attempts information required for determining if a outbound payment can
@@ -411,6 +421,109 @@ impl OutboundPayments {
411
421
}
412
422
}
413
423
424
+ pub ( super ) fn check_retry_payments < R : Deref , ES : Deref , NS : Deref , SP , IH , FH , L : Deref > (
425
+ & self , router : & R , first_hops : FH , inflight_htlcs : IH , entropy_source : & ES , node_signer : & NS ,
426
+ best_block_height : u32 , logger : & L , send_payment_along_path : SP ,
427
+ )
428
+ where
429
+ R :: Target : Router ,
430
+ ES :: Target : EntropySource ,
431
+ NS :: Target : NodeSigner ,
432
+ SP : Fn ( & Vec < RouteHop > , & Option < PaymentParameters > , & PaymentHash , & Option < PaymentSecret > , u64 ,
433
+ u32 , PaymentId , & Option < PaymentPreimage > , [ u8 ; 32 ] ) -> Result < ( ) , APIError > ,
434
+ IH : Fn ( ) -> InFlightHtlcs ,
435
+ FH : Fn ( ) -> Vec < ChannelDetails > ,
436
+ L :: Target : Logger ,
437
+ {
438
+ loop {
439
+ let mut outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
440
+ let mut retry_id_route_params = None ;
441
+ for ( pmt_id, pmt) in outbounds. iter_mut ( ) {
442
+ if pmt. is_retryable_now ( ) {
443
+ if let PendingOutboundPayment :: Retryable { pending_amt_msat, total_msat, route_params : Some ( params) , .. } = pmt {
444
+ if pending_amt_msat < total_msat {
445
+ retry_id_route_params = Some ( ( * pmt_id, params. clone ( ) ) ) ;
446
+ pmt. increment_attempts ( ) ;
447
+ break
448
+ }
449
+ }
450
+ }
451
+ }
452
+ if let Some ( ( payment_id, route_params) ) = retry_id_route_params {
453
+ core:: mem:: drop ( outbounds) ;
454
+ if let Err ( e) = self . pay_internal ( payment_id, route_params, router, first_hops ( ) , inflight_htlcs ( ) , entropy_source, node_signer, best_block_height, & send_payment_along_path) {
455
+ log_trace ! ( logger, "Errored retrying payment: {:?}" , e) ;
456
+ }
457
+ } else { break }
458
+ }
459
+ }
460
+
461
+ fn pay_internal < R : Deref , NS : Deref , ES : Deref , F > (
462
+ & self , payment_id : PaymentId , route_params : RouteParameters , router : & R ,
463
+ first_hops : Vec < ChannelDetails > , inflight_htlcs : InFlightHtlcs , entropy_source : & ES ,
464
+ node_signer : & NS , best_block_height : u32 , send_payment_along_path : & F
465
+ ) -> Result < ( ) , PaymentSendFailure >
466
+ where
467
+ R :: Target : Router ,
468
+ ES :: Target : EntropySource ,
469
+ NS :: Target : NodeSigner ,
470
+ F : Fn ( & Vec < RouteHop > , & Option < PaymentParameters > , & PaymentHash , & Option < PaymentSecret > , u64 ,
471
+ u32 , PaymentId , & Option < PaymentPreimage > , [ u8 ; 32 ] ) -> Result < ( ) , APIError >
472
+ {
473
+ #[ cfg( feature = "std" ) ] {
474
+ if has_expired ( & route_params) {
475
+ return Err ( PaymentSendFailure :: ParameterError ( APIError :: APIMisuseError {
476
+ err : format ! ( "Invoice expired for payment id {}" , log_bytes!( payment_id. 0 ) ) ,
477
+ } ) )
478
+ }
479
+ }
480
+
481
+ let route = router. find_route (
482
+ & node_signer. get_node_id ( Recipient :: Node ) . unwrap ( ) , & route_params,
483
+ Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) ) , & inflight_htlcs
484
+ ) . map_err ( |e| PaymentSendFailure :: ParameterError ( APIError :: APIMisuseError {
485
+ err : format ! ( "Failed to find a route for payment {}: {:?}" , log_bytes!( payment_id. 0 ) , e) , // TODO: add APIError::RouteNotFound
486
+ } ) ) ?;
487
+
488
+ let res = self . retry_payment_with_route ( & route, payment_id, entropy_source, node_signer, best_block_height, send_payment_along_path) ;
489
+ match res {
490
+ Err ( PaymentSendFailure :: AllFailedResendSafe ( _) ) => {
491
+ let mut outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
492
+ if let Some ( payment) = outbounds. get_mut ( & payment_id) {
493
+ let retryable = payment. is_retryable_now ( ) ;
494
+ if retryable {
495
+ payment. increment_attempts ( ) ;
496
+ } else { return res }
497
+ } else { return res }
498
+ core:: mem:: drop ( outbounds) ;
499
+ self . pay_internal ( payment_id, route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, send_payment_along_path)
500
+ } ,
501
+ Err ( PaymentSendFailure :: PartialFailure { failed_paths_retry : Some ( retry) , results, .. } ) => {
502
+ let mut outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
503
+ if let Some ( payment) = outbounds. get_mut ( & payment_id) {
504
+ let retryable = payment. is_retryable_now ( ) ;
505
+ if retryable {
506
+ payment. increment_attempts ( ) ;
507
+ } else { return Err ( PaymentSendFailure :: PartialFailure { failed_paths_retry : Some ( retry) , results, payment_id } ) }
508
+ } else { return Err ( PaymentSendFailure :: PartialFailure { failed_paths_retry : Some ( retry) , results, payment_id } ) }
509
+ core:: mem:: drop ( outbounds) ;
510
+
511
+ // Some paths were sent, even if we failed to send the full MPP value our recipient may
512
+ // misbehave and claim the funds, at which point we have to consider the payment sent, so
513
+ // return `Ok()` here, ignoring any retry errors.
514
+ let _ = self . pay_internal ( payment_id, retry, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, send_payment_along_path) ;
515
+ Ok ( ( ) )
516
+ } ,
517
+ Err ( PaymentSendFailure :: PartialFailure { failed_paths_retry : None , .. } ) => {
518
+ // This may happen if we send a payment and some paths fail, but only due to a temporary
519
+ // monitor failure or the like, implying they're really in-flight, but we haven't sent the
520
+ // initial HTLC-Add messages yet.
521
+ Ok ( ( ) )
522
+ } ,
523
+ res => res,
524
+ }
525
+ }
526
+
414
527
pub ( super ) fn retry_payment_with_route < ES : Deref , NS : Deref , F > (
415
528
& self , route : & Route , payment_id : PaymentId , entropy_source : & ES , node_signer : & NS , best_block_height : u32 ,
416
529
send_payment_along_path : F
0 commit comments