@@ -41,7 +41,7 @@ pub(crate) enum PendingOutboundPayment {
4141 session_privs : HashSet < [ u8 ; 32 ] > ,
4242 } ,
4343 Retryable {
44- retry_strategy : Retry ,
44+ retry_strategy : Option < Retry > ,
4545 attempts : PaymentAttempts ,
4646 route_params : Option < RouteParameters > ,
4747 session_privs : HashSet < [ u8 ; 32 ] > ,
@@ -82,11 +82,25 @@ impl PendingOutboundPayment {
8282 attempts. count += 1 ;
8383 }
8484 }
85+ fn is_auto_retryable_now ( & self ) -> bool {
86+ match self {
87+ PendingOutboundPayment :: Retryable { retry_strategy : Some ( strategy) , attempts, .. } => {
88+ strategy. is_retryable_now ( & attempts)
89+ } ,
90+ _ => false ,
91+ }
92+ }
8593 fn is_retryable_now ( & self ) -> bool {
86- if let PendingOutboundPayment :: Retryable { retry_strategy, attempts, .. } = self {
87- return retry_strategy. is_retryable_now ( & attempts)
94+ match self {
95+ PendingOutboundPayment :: Retryable { retry_strategy : None , .. } => {
96+ // We're handling retries manually, we can always retry.
97+ true
98+ } ,
99+ PendingOutboundPayment :: Retryable { retry_strategy : Some ( strategy) , attempts, .. } => {
100+ strategy. is_retryable_now ( & attempts)
101+ } ,
102+ _ => false ,
88103 }
89- false
90104 }
91105 pub fn insert_previously_failed_scid ( & mut self , scid : u64 ) {
92106 if let PendingOutboundPayment :: Retryable { route_params : Some ( params) , .. } = self {
@@ -212,9 +226,9 @@ impl PendingOutboundPayment {
212226pub enum Retry {
213227 /// Max number of attempts to retry payment.
214228 ///
215- /// Note that this is the number of *path* failures, not full payment retries. For multi-path
216- /// payments, if this is less than the total number of paths, we will never even retry all of the
217- /// payment's paths .
229+ /// Each attempt may be multiple HTLCs along multiple paths if the router decides to split up a
230+ /// retry, and may retry multiple failed HTLCs at once if they failed around the same time and
231+ /// were retried along a route from a single call to [`Router::find_route`] .
218232 Attempts ( usize ) ,
219233 #[ cfg( not( feature = "no-std" ) ) ]
220234 /// Time elapsed before abandoning retries for a payment.
@@ -409,7 +423,7 @@ impl OutboundPayments {
409423 F : Fn ( & Vec < RouteHop > , & Option < PaymentParameters > , & PaymentHash , & Option < PaymentSecret > , u64 ,
410424 u32 , PaymentId , & Option < PaymentPreimage > , [ u8 ; 32 ] ) -> Result < ( ) , APIError >
411425 {
412- let onion_session_privs = self . add_new_pending_payment ( payment_hash, * payment_secret, payment_id, route, Retry :: Attempts ( 0 ) , None , entropy_source, best_block_height) ?;
426+ let onion_session_privs = self . add_new_pending_payment ( payment_hash, * payment_secret, payment_id, route, None , None , entropy_source, best_block_height) ?;
413427 self . pay_route_internal ( route, payment_hash, payment_secret, None , payment_id, None ,
414428 onion_session_privs, node_signer, best_block_height, & send_payment_along_path)
415429 . map_err ( |e| { self . remove_outbound_if_all_failed ( payment_id, & e) ; e } )
@@ -430,7 +444,7 @@ impl OutboundPayments {
430444 None => PaymentPreimage ( entropy_source. get_secure_random_bytes ( ) ) ,
431445 } ;
432446 let payment_hash = PaymentHash ( Sha256 :: hash ( & preimage. 0 ) . into_inner ( ) ) ;
433- let onion_session_privs = self . add_new_pending_payment ( payment_hash, None , payment_id, & route, Retry :: Attempts ( 0 ) , None , entropy_source, best_block_height) ?;
447+ let onion_session_privs = self . add_new_pending_payment ( payment_hash, None , payment_id, & route, None , None , entropy_source, best_block_height) ?;
434448
435449 match self . pay_route_internal ( route, payment_hash, & None , Some ( preimage) , payment_id, None , onion_session_privs, node_signer, best_block_height, & send_payment_along_path) {
436450 Ok ( ( ) ) => Ok ( payment_hash) ,
@@ -459,11 +473,10 @@ impl OutboundPayments {
459473 let mut outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
460474 let mut retry_id_route_params = None ;
461475 for ( pmt_id, pmt) in outbounds. iter_mut ( ) {
462- if pmt. is_retryable_now ( ) {
476+ if pmt. is_auto_retryable_now ( ) {
463477 if let PendingOutboundPayment :: Retryable { pending_amt_msat, total_msat, route_params : Some ( params) , .. } = pmt {
464478 if pending_amt_msat < total_msat {
465479 retry_id_route_params = Some ( ( * pmt_id, params. clone ( ) ) ) ;
466- pmt. increment_attempts ( ) ;
467480 break
468481 }
469482 }
@@ -509,35 +522,21 @@ impl OutboundPayments {
509522 } ) ) ?;
510523
511524 let res = if let Some ( ( payment_hash, payment_secret, retry_strategy) ) = initial_send_info {
512- let onion_session_privs = self . add_new_pending_payment ( payment_hash, * payment_secret, payment_id, & route, retry_strategy, Some ( route_params. clone ( ) ) , entropy_source, best_block_height) ?;
525+ let onion_session_privs = self . add_new_pending_payment ( payment_hash, * payment_secret, payment_id, & route, Some ( retry_strategy) , Some ( route_params. clone ( ) ) , entropy_source, best_block_height) ?;
513526 self . pay_route_internal ( & route, payment_hash, payment_secret, None , payment_id, None , onion_session_privs, node_signer, best_block_height, send_payment_along_path)
514527 } else {
515528 self . retry_payment_with_route ( & route, payment_id, entropy_source, node_signer, best_block_height, send_payment_along_path)
516529 } ;
517530 match res {
518531 Err ( PaymentSendFailure :: AllFailedResendSafe ( _) ) => {
519- let mut outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
520- if let Some ( payment) = outbounds. get_mut ( & payment_id) {
521- let retryable = payment. is_retryable_now ( ) ;
522- if retryable {
523- payment. increment_attempts ( ) ;
524- } else { return res }
525- } else { return res }
526- core:: mem:: drop ( outbounds) ;
527532 let retry_res = self . pay_internal ( payment_id, None , route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, logger, send_payment_along_path) ;
528533 log_info ! ( logger, "Result retrying payment id {}: {:?}" , log_bytes!( payment_id. 0 ) , retry_res) ;
534+ if let Err ( PaymentSendFailure :: ParameterError ( APIError :: APIMisuseError { err } ) ) = & retry_res {
535+ if err. starts_with ( "Retries exhausted " ) { return res; }
536+ }
529537 retry_res
530538 } ,
531- Err ( PaymentSendFailure :: PartialFailure { failed_paths_retry : Some ( retry) , results, .. } ) => {
532- let mut outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
533- if let Some ( payment) = outbounds. get_mut ( & payment_id) {
534- let retryable = payment. is_retryable_now ( ) ;
535- if retryable {
536- payment. increment_attempts ( ) ;
537- } else { return Err ( PaymentSendFailure :: PartialFailure { failed_paths_retry : Some ( retry) , results, payment_id } ) }
538- } else { return Err ( PaymentSendFailure :: PartialFailure { failed_paths_retry : Some ( retry) , results, payment_id } ) }
539- core:: mem:: drop ( outbounds) ;
540-
539+ Err ( PaymentSendFailure :: PartialFailure { failed_paths_retry : Some ( retry) , .. } ) => {
541540 // Some paths were sent, even if we failed to send the full MPP value our recipient may
542541 // misbehave and claim the funds, at which point we have to consider the payment sent, so
543542 // return `Ok()` here, ignoring any retry errors.
@@ -611,6 +610,12 @@ impl OutboundPayments {
611610 } ) ) ;
612611 } ,
613612 } ;
613+ if !payment. is_retryable_now ( ) {
614+ return Err ( PaymentSendFailure :: ParameterError ( APIError :: APIMisuseError {
615+ err : format ! ( "Retries exhausted for payment id {}" , log_bytes!( payment_id. 0 ) ) ,
616+ } ) )
617+ }
618+ payment. increment_attempts ( ) ;
614619 for ( path, session_priv_bytes) in route. paths . iter ( ) . zip ( onion_session_privs. iter ( ) ) {
615620 assert ! ( payment. insert( * session_priv_bytes, path) ) ;
616621 }
@@ -646,7 +651,7 @@ impl OutboundPayments {
646651 }
647652
648653 let route = Route { paths : vec ! [ hops] , payment_params : None } ;
649- let onion_session_privs = self . add_new_pending_payment ( payment_hash, None , payment_id, & route, Retry :: Attempts ( 0 ) , None , entropy_source, best_block_height) ?;
654+ let onion_session_privs = self . add_new_pending_payment ( payment_hash, None , payment_id, & route, None , None , entropy_source, best_block_height) ?;
650655
651656 match self . pay_route_internal ( & route, payment_hash, & None , None , payment_id, None , onion_session_privs, node_signer, best_block_height, & send_payment_along_path) {
652657 Ok ( ( ) ) => Ok ( ( payment_hash, payment_id) ) ,
@@ -660,14 +665,14 @@ impl OutboundPayments {
660665 #[ cfg( test) ]
661666 pub ( super ) fn test_add_new_pending_payment < ES : Deref > (
662667 & self , payment_hash : PaymentHash , payment_secret : Option < PaymentSecret > , payment_id : PaymentId ,
663- route : & Route , retry_strategy : Retry , entropy_source : & ES , best_block_height : u32
668+ route : & Route , retry_strategy : Option < Retry > , entropy_source : & ES , best_block_height : u32
664669 ) -> Result < Vec < [ u8 ; 32 ] > , PaymentSendFailure > where ES :: Target : EntropySource {
665670 self . add_new_pending_payment ( payment_hash, payment_secret, payment_id, route, retry_strategy, None , entropy_source, best_block_height)
666671 }
667672
668673 pub ( super ) fn add_new_pending_payment < ES : Deref > (
669674 & self , payment_hash : PaymentHash , payment_secret : Option < PaymentSecret > , payment_id : PaymentId ,
670- route : & Route , retry_strategy : Retry , route_params : Option < RouteParameters > ,
675+ route : & Route , retry_strategy : Option < Retry > , route_params : Option < RouteParameters > ,
671676 entropy_source : & ES , best_block_height : u32
672677 ) -> Result < Vec < [ u8 ; 32 ] > , PaymentSendFailure > where ES :: Target : EntropySource {
673678 let mut onion_session_privs = Vec :: with_capacity ( route. paths . len ( ) ) ;
@@ -969,7 +974,7 @@ impl OutboundPayments {
969974 log_trace ! ( logger, "Received failure of HTLC with payment_hash {} after payment completion" , log_bytes!( payment_hash. 0 ) ) ;
970975 return
971976 }
972- let is_retryable_now = payment. get ( ) . is_retryable_now ( ) ;
977+ let is_retryable_now = payment. get ( ) . is_auto_retryable_now ( ) ;
973978 if let Some ( scid) = short_channel_id {
974979 payment. get_mut ( ) . insert_previously_failed_scid ( scid) ;
975980 }
@@ -1110,7 +1115,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
11101115 ( 0 , session_privs, required) ,
11111116 ( 1 , pending_fee_msat, option) ,
11121117 ( 2 , payment_hash, required) ,
1113- ( not_written, retry_strategy, ( static_value, Retry :: Attempts ( 0 ) ) ) ,
1118+ ( not_written, retry_strategy, ( static_value, None ) ) ,
11141119 ( 4 , payment_secret, option) ,
11151120 ( not_written, attempts, ( static_value, PaymentAttempts :: new( ) ) ) ,
11161121 ( 6 , total_msat, required) ,
@@ -1207,7 +1212,7 @@ mod tests {
12071212
12081213 let err = if on_retry {
12091214 outbound_payments. add_new_pending_payment ( PaymentHash ( [ 0 ; 32 ] ) , None , PaymentId ( [ 0 ; 32 ] ) ,
1210- & Route { paths : vec ! [ ] , payment_params : None } , Retry :: Attempts ( 1 ) , Some ( route_params. clone ( ) ) ,
1215+ & Route { paths : vec ! [ ] , payment_params : None } , Some ( Retry :: Attempts ( 1 ) ) , Some ( route_params. clone ( ) ) ,
12111216 & & keys_manager, 0 ) . unwrap ( ) ;
12121217 outbound_payments. pay_internal (
12131218 PaymentId ( [ 0 ; 32 ] ) , None , route_params, & & router, vec ! [ ] , InFlightHtlcs :: new ( ) ,
0 commit comments