@@ -41,7 +41,7 @@ pub(crate) enum PendingOutboundPayment {
41
41
session_privs : HashSet < [ u8 ; 32 ] > ,
42
42
} ,
43
43
Retryable {
44
- retry_strategy : Retry ,
44
+ retry_strategy : Option < Retry > ,
45
45
attempts : PaymentAttempts ,
46
46
route_params : Option < RouteParameters > ,
47
47
session_privs : HashSet < [ u8 ; 32 ] > ,
@@ -82,11 +82,25 @@ impl PendingOutboundPayment {
82
82
attempts. count += 1 ;
83
83
}
84
84
}
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
+ }
85
93
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 ,
88
103
}
89
- false
90
104
}
91
105
pub fn insert_previously_failed_scid ( & mut self , scid : u64 ) {
92
106
if let PendingOutboundPayment :: Retryable { route_params : Some ( params) , .. } = self {
@@ -212,9 +226,9 @@ impl PendingOutboundPayment {
212
226
pub enum Retry {
213
227
/// Max number of attempts to retry payment.
214
228
///
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`] .
218
232
Attempts ( usize ) ,
219
233
#[ cfg( not( feature = "no-std" ) ) ]
220
234
/// Time elapsed before abandoning retries for a payment.
@@ -409,7 +423,7 @@ impl OutboundPayments {
409
423
F : Fn ( & Vec < RouteHop > , & Option < PaymentParameters > , & PaymentHash , & Option < PaymentSecret > , u64 ,
410
424
u32 , PaymentId , & Option < PaymentPreimage > , [ u8 ; 32 ] ) -> Result < ( ) , APIError >
411
425
{
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) ?;
413
427
self . pay_route_internal ( route, payment_hash, payment_secret, None , payment_id, None ,
414
428
onion_session_privs, node_signer, best_block_height, & send_payment_along_path)
415
429
. map_err ( |e| { self . remove_outbound_if_all_failed ( payment_id, & e) ; e } )
@@ -430,7 +444,7 @@ impl OutboundPayments {
430
444
None => PaymentPreimage ( entropy_source. get_secure_random_bytes ( ) ) ,
431
445
} ;
432
446
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) ?;
434
448
435
449
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) {
436
450
Ok ( ( ) ) => Ok ( payment_hash) ,
@@ -459,11 +473,10 @@ impl OutboundPayments {
459
473
let mut outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
460
474
let mut retry_id_route_params = None ;
461
475
for ( pmt_id, pmt) in outbounds. iter_mut ( ) {
462
- if pmt. is_retryable_now ( ) {
476
+ if pmt. is_auto_retryable_now ( ) {
463
477
if let PendingOutboundPayment :: Retryable { pending_amt_msat, total_msat, route_params : Some ( params) , .. } = pmt {
464
478
if pending_amt_msat < total_msat {
465
479
retry_id_route_params = Some ( ( * pmt_id, params. clone ( ) ) ) ;
466
- pmt. increment_attempts ( ) ;
467
480
break
468
481
}
469
482
}
@@ -509,35 +522,21 @@ impl OutboundPayments {
509
522
} ) ) ?;
510
523
511
524
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) ?;
513
526
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)
514
527
} else {
515
528
self . retry_payment_with_route ( & route, payment_id, entropy_source, node_signer, best_block_height, send_payment_along_path)
516
529
} ;
517
530
match res {
518
531
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) ;
527
532
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) ;
528
533
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
+ }
529
537
retry_res
530
538
} ,
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) , .. } ) => {
541
540
// Some paths were sent, even if we failed to send the full MPP value our recipient may
542
541
// misbehave and claim the funds, at which point we have to consider the payment sent, so
543
542
// return `Ok()` here, ignoring any retry errors.
@@ -611,6 +610,12 @@ impl OutboundPayments {
611
610
} ) ) ;
612
611
} ,
613
612
} ;
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 ( ) ;
614
619
for ( path, session_priv_bytes) in route. paths . iter ( ) . zip ( onion_session_privs. iter ( ) ) {
615
620
assert ! ( payment. insert( * session_priv_bytes, path) ) ;
616
621
}
@@ -646,7 +651,7 @@ impl OutboundPayments {
646
651
}
647
652
648
653
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) ?;
650
655
651
656
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) {
652
657
Ok ( ( ) ) => Ok ( ( payment_hash, payment_id) ) ,
@@ -660,14 +665,14 @@ impl OutboundPayments {
660
665
#[ cfg( test) ]
661
666
pub ( super ) fn test_add_new_pending_payment < ES : Deref > (
662
667
& 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
664
669
) -> Result < Vec < [ u8 ; 32 ] > , PaymentSendFailure > where ES :: Target : EntropySource {
665
670
self . add_new_pending_payment ( payment_hash, payment_secret, payment_id, route, retry_strategy, None , entropy_source, best_block_height)
666
671
}
667
672
668
673
pub ( super ) fn add_new_pending_payment < ES : Deref > (
669
674
& 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 > ,
671
676
entropy_source : & ES , best_block_height : u32
672
677
) -> Result < Vec < [ u8 ; 32 ] > , PaymentSendFailure > where ES :: Target : EntropySource {
673
678
let mut onion_session_privs = Vec :: with_capacity ( route. paths . len ( ) ) ;
@@ -969,7 +974,7 @@ impl OutboundPayments {
969
974
log_trace ! ( logger, "Received failure of HTLC with payment_hash {} after payment completion" , log_bytes!( payment_hash. 0 ) ) ;
970
975
return
971
976
}
972
- let is_retryable_now = payment. get ( ) . is_retryable_now ( ) ;
977
+ let is_retryable_now = payment. get ( ) . is_auto_retryable_now ( ) ;
973
978
if let Some ( scid) = short_channel_id {
974
979
payment. get_mut ( ) . insert_previously_failed_scid ( scid) ;
975
980
}
@@ -1110,7 +1115,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
1110
1115
( 0 , session_privs, required) ,
1111
1116
( 1 , pending_fee_msat, option) ,
1112
1117
( 2 , payment_hash, required) ,
1113
- ( not_written, retry_strategy, ( static_value, Retry :: Attempts ( 0 ) ) ) ,
1118
+ ( not_written, retry_strategy, ( static_value, None ) ) ,
1114
1119
( 4 , payment_secret, option) ,
1115
1120
( not_written, attempts, ( static_value, PaymentAttempts :: new( ) ) ) ,
1116
1121
( 6 , total_msat, required) ,
@@ -1207,7 +1212,7 @@ mod tests {
1207
1212
1208
1213
let err = if on_retry {
1209
1214
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 ( ) ) ,
1211
1216
& & keys_manager, 0 ) . unwrap ( ) ;
1212
1217
outbound_payments. pay_internal (
1213
1218
PaymentId ( [ 0 ; 32 ] ) , None , route_params, & & router, vec ! [ ] , InFlightHtlcs :: new ( ) ,
0 commit comments