@@ -524,9 +524,9 @@ impl OutboundPayments {
524
524
let mut retry_id_route_params = None ;
525
525
for ( pmt_id, pmt) in outbounds. iter_mut ( ) {
526
526
if pmt. is_auto_retryable_now ( ) {
527
- if let PendingOutboundPayment :: Retryable { payment_hash , pending_amt_msat, total_msat, payment_params : Some ( params) , .. } = pmt {
527
+ if let PendingOutboundPayment :: Retryable { pending_amt_msat, total_msat, payment_params : Some ( params) , .. } = pmt {
528
528
if pending_amt_msat < total_msat {
529
- retry_id_route_params = Some ( ( * pmt_id, * payment_hash , RouteParameters {
529
+ retry_id_route_params = Some ( ( * pmt_id, RouteParameters {
530
530
final_value_msat : * total_msat - * pending_amt_msat,
531
531
final_cltv_expiry_delta :
532
532
if let Some ( delta) = params. final_cltv_expiry_delta { delta }
@@ -542,8 +542,8 @@ impl OutboundPayments {
542
542
}
543
543
}
544
544
core:: mem:: drop ( outbounds) ;
545
- if let Some ( ( payment_id, payment_hash , route_params) ) = retry_id_route_params {
546
- self . retry_payment_internal ( payment_id, payment_hash , route_params, router, first_hops ( ) , & inflight_htlcs, entropy_source, node_signer, best_block_height, logger, pending_events, & send_payment_along_path)
545
+ if let Some ( ( payment_id, route_params) ) = retry_id_route_params {
546
+ self . retry_payment_internal ( payment_id, route_params, router, first_hops ( ) , & inflight_htlcs, entropy_source, node_signer, best_block_height, logger, pending_events, & send_payment_along_path)
547
547
} else { break }
548
548
}
549
549
@@ -616,10 +616,10 @@ impl OutboundPayments {
616
616
}
617
617
618
618
fn retry_payment_internal < R : Deref , NS : Deref , ES : Deref , IH , SP , L : Deref > (
619
- & self , payment_id : PaymentId , payment_hash : PaymentHash , route_params : RouteParameters ,
620
- router : & R , first_hops : Vec < ChannelDetails > , inflight_htlcs : & IH , entropy_source : & ES ,
621
- node_signer : & NS , best_block_height : u32 , logger : & L ,
622
- pending_events : & Mutex < Vec < events :: Event > > , send_payment_along_path : & SP ,
619
+ & self , payment_id : PaymentId , route_params : RouteParameters , router : & R ,
620
+ first_hops : Vec < ChannelDetails > , inflight_htlcs : & IH , entropy_source : & ES , node_signer : & NS ,
621
+ best_block_height : u32 , logger : & L , pending_events : & Mutex < Vec < events :: Event > > ,
622
+ send_payment_along_path : & SP ,
623
623
)
624
624
where
625
625
R :: Target : Router ,
@@ -649,9 +649,80 @@ impl OutboundPayments {
649
649
return
650
650
}
651
651
} ;
652
+ for path in route. paths . iter ( ) {
653
+ if path. len ( ) == 0 {
654
+ log_error ! ( logger, "length-0 path in route" ) ;
655
+ self . abandon_payment ( payment_id, pending_events) ;
656
+ return
657
+ }
658
+ }
652
659
653
- let res = self . retry_payment_with_route ( & route, payment_id, entropy_source, node_signer,
654
- best_block_height, send_payment_along_path) ;
660
+ const RETRY_OVERFLOW_PERCENTAGE : u64 = 10 ;
661
+ let mut onion_session_privs = Vec :: with_capacity ( route. paths . len ( ) ) ;
662
+ for _ in 0 ..route. paths . len ( ) {
663
+ onion_session_privs. push ( entropy_source. get_secure_random_bytes ( ) ) ;
664
+ }
665
+
666
+ macro_rules! abandon_with_entry {
667
+ ( $payment_id: expr, $payment: expr, $pending_events: expr) => {
668
+ if $payment. get_mut( ) . mark_abandoned( ) . is_ok( ) && $payment. get( ) . remaining_parts( ) == 0 {
669
+ $pending_events. lock( ) . unwrap( ) . push( events:: Event :: PaymentFailed {
670
+ payment_id: $payment_id,
671
+ payment_hash: $payment. get( ) . payment_hash( ) . expect( "PendingOutboundPayments::RetriesExceeded always has a payment hash set" ) ,
672
+ } ) ;
673
+ $payment. remove( ) ;
674
+ }
675
+ }
676
+ }
677
+ let ( total_msat, payment_hash, payment_secret, keysend_preimage) = {
678
+ let mut outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
679
+ match outbounds. entry ( payment_id) {
680
+ hash_map:: Entry :: Occupied ( mut payment) => {
681
+ let res = match payment. get_mut ( ) {
682
+ PendingOutboundPayment :: Retryable {
683
+ total_msat, payment_hash, keysend_preimage, payment_secret, pending_amt_msat, ..
684
+ } => {
685
+ let retry_amt_msat: u64 = route. paths . iter ( ) . map ( |path| path. last ( ) . unwrap ( ) . fee_msat ) . sum ( ) ;
686
+ if retry_amt_msat + * pending_amt_msat > * total_msat * ( 100 + RETRY_OVERFLOW_PERCENTAGE ) / 100 {
687
+ log_error ! ( logger, "retry_amt_msat of {} will put pending_amt_msat (currently: {}) more than 10% over total_payment_amt_msat of {}" , retry_amt_msat, pending_amt_msat, total_msat) ;
688
+ abandon_with_entry ! ( payment_id, payment, pending_events) ;
689
+ return
690
+ }
691
+ ( * total_msat, * payment_hash, * payment_secret, * keysend_preimage)
692
+ } ,
693
+ PendingOutboundPayment :: Legacy { .. } => {
694
+ log_error ! ( logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102" ) ;
695
+ return
696
+ } ,
697
+ PendingOutboundPayment :: Fulfilled { .. } => {
698
+ log_error ! ( logger, "Payment already completed" ) ;
699
+ return
700
+ } ,
701
+ PendingOutboundPayment :: Abandoned { .. } => {
702
+ log_error ! ( logger, "Payment already abandoned (with some HTLCs still pending)" ) ;
703
+ return
704
+ } ,
705
+ } ;
706
+ if !payment. get ( ) . is_retryable_now ( ) {
707
+ log_error ! ( logger, "Retries exhausted for payment id {}" , log_bytes!( payment_id. 0 ) ) ;
708
+ abandon_with_entry ! ( payment_id, payment, pending_events) ;
709
+ return
710
+ }
711
+ payment. get_mut ( ) . increment_attempts ( ) ;
712
+ for ( path, session_priv_bytes) in route. paths . iter ( ) . zip ( onion_session_privs. iter ( ) ) {
713
+ assert ! ( payment. get_mut( ) . insert( * session_priv_bytes, path) ) ;
714
+ }
715
+ res
716
+ } ,
717
+ hash_map:: Entry :: Vacant ( _) => {
718
+ log_error ! ( logger, "Payment with ID {} not found" , log_bytes!( payment_id. 0 ) ) ;
719
+ return
720
+ }
721
+ }
722
+ } ;
723
+ let res = self . pay_route_internal ( & route, payment_hash, & payment_secret, keysend_preimage,
724
+ payment_id, Some ( total_msat) , onion_session_privs, node_signer, best_block_height,
725
+ & send_payment_along_path) ;
655
726
log_info ! ( logger, "Result retrying payment id {}: {:?}" , log_bytes!( payment_id. 0 ) , res) ;
656
727
if let Err ( e) = res {
657
728
self . handle_pay_route_err ( e, payment_id, payment_hash, route, route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, logger, pending_events, send_payment_along_path) ;
@@ -676,14 +747,14 @@ impl OutboundPayments {
676
747
match err {
677
748
PaymentSendFailure :: AllFailedResendSafe ( errs) => {
678
749
Self :: push_payment_path_failed_evs ( payment_id, payment_hash, route. paths , errs. into_iter ( ) . map ( |e| Err ( e) ) , pending_events) ;
679
- self . retry_payment_internal ( payment_id, payment_hash , route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, logger, pending_events, send_payment_along_path) ;
750
+ self . retry_payment_internal ( payment_id, route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, logger, pending_events, send_payment_along_path) ;
680
751
} ,
681
752
PaymentSendFailure :: PartialFailure { failed_paths_retry : Some ( retry) , results, .. } => {
682
753
Self :: push_payment_path_failed_evs ( payment_id, payment_hash, route. paths , results. into_iter ( ) , pending_events) ;
683
754
// Some paths were sent, even if we failed to send the full MPP value our recipient may
684
755
// misbehave and claim the funds, at which point we have to consider the payment sent, so
685
756
// return `Ok()` here, ignoring any retry errors.
686
- self . retry_payment_internal ( payment_id, payment_hash , retry, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, logger, pending_events, send_payment_along_path) ;
757
+ self . retry_payment_internal ( payment_id, retry, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, logger, pending_events, send_payment_along_path) ;
687
758
} ,
688
759
PaymentSendFailure :: PartialFailure { failed_paths_retry : None , .. } => {
689
760
// This may happen if we send a payment and some paths fail, but only due to a temporary
@@ -729,82 +800,6 @@ impl OutboundPayments {
729
800
}
730
801
}
731
802
732
- pub ( super ) fn retry_payment_with_route < ES : Deref , NS : Deref , F > (
733
- & self , route : & Route , payment_id : PaymentId , entropy_source : & ES , node_signer : & NS , best_block_height : u32 ,
734
- send_payment_along_path : F
735
- ) -> Result < ( ) , PaymentSendFailure >
736
- where
737
- ES :: Target : EntropySource ,
738
- NS :: Target : NodeSigner ,
739
- F : Fn ( & Vec < RouteHop > , & Option < PaymentParameters > , & PaymentHash , & Option < PaymentSecret > , u64 ,
740
- u32 , PaymentId , & Option < PaymentPreimage > , [ u8 ; 32 ] ) -> Result < ( ) , APIError >
741
- {
742
- const RETRY_OVERFLOW_PERCENTAGE : u64 = 10 ;
743
- for path in route. paths . iter ( ) {
744
- if path. len ( ) == 0 {
745
- return Err ( PaymentSendFailure :: ParameterError ( APIError :: APIMisuseError {
746
- err : "length-0 path in route" . to_string ( )
747
- } ) )
748
- }
749
- }
750
-
751
- let mut onion_session_privs = Vec :: with_capacity ( route. paths . len ( ) ) ;
752
- for _ in 0 ..route. paths . len ( ) {
753
- onion_session_privs. push ( entropy_source. get_secure_random_bytes ( ) ) ;
754
- }
755
-
756
- let ( total_msat, payment_hash, payment_secret, keysend_preimage) = {
757
- let mut outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
758
- match outbounds. get_mut ( & payment_id) {
759
- Some ( payment) => {
760
- let res = match payment {
761
- PendingOutboundPayment :: Retryable {
762
- total_msat, payment_hash, keysend_preimage, payment_secret, pending_amt_msat, ..
763
- } => {
764
- let retry_amt_msat: u64 = route. paths . iter ( ) . map ( |path| path. last ( ) . unwrap ( ) . fee_msat ) . sum ( ) ;
765
- if retry_amt_msat + * pending_amt_msat > * total_msat * ( 100 + RETRY_OVERFLOW_PERCENTAGE ) / 100 {
766
- return Err ( PaymentSendFailure :: ParameterError ( APIError :: APIMisuseError {
767
- err : format ! ( "retry_amt_msat of {} will put pending_amt_msat (currently: {}) more than 10% over total_payment_amt_msat of {}" , retry_amt_msat, pending_amt_msat, total_msat) . to_string ( )
768
- } ) )
769
- }
770
- ( * total_msat, * payment_hash, * payment_secret, * keysend_preimage)
771
- } ,
772
- PendingOutboundPayment :: Legacy { .. } => {
773
- return Err ( PaymentSendFailure :: ParameterError ( APIError :: APIMisuseError {
774
- err : "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102" . to_string ( )
775
- } ) )
776
- } ,
777
- PendingOutboundPayment :: Fulfilled { .. } => {
778
- return Err ( PaymentSendFailure :: ParameterError ( APIError :: APIMisuseError {
779
- err : "Payment already completed" . to_owned ( )
780
- } ) ) ;
781
- } ,
782
- PendingOutboundPayment :: Abandoned { .. } => {
783
- return Err ( PaymentSendFailure :: ParameterError ( APIError :: APIMisuseError {
784
- err : "Payment already abandoned (with some HTLCs still pending)" . to_owned ( )
785
- } ) ) ;
786
- } ,
787
- } ;
788
- if !payment. is_retryable_now ( ) {
789
- return Err ( PaymentSendFailure :: ParameterError ( APIError :: APIMisuseError {
790
- err : format ! ( "Retries exhausted for payment id {}" , log_bytes!( payment_id. 0 ) ) ,
791
- } ) )
792
- }
793
- payment. increment_attempts ( ) ;
794
- for ( path, session_priv_bytes) in route. paths . iter ( ) . zip ( onion_session_privs. iter ( ) ) {
795
- assert ! ( payment. insert( * session_priv_bytes, path) ) ;
796
- }
797
- res
798
- } ,
799
- None =>
800
- return Err ( PaymentSendFailure :: ParameterError ( APIError :: APIMisuseError {
801
- err : format ! ( "Payment with ID {} not found" , log_bytes!( payment_id. 0 ) ) ,
802
- } ) ) ,
803
- }
804
- } ;
805
- self . pay_route_internal ( route, payment_hash, & payment_secret, keysend_preimage, payment_id, Some ( total_msat) , onion_session_privs, node_signer, best_block_height, & send_payment_along_path)
806
- }
807
-
808
803
pub ( super ) fn send_probe < ES : Deref , NS : Deref , F > (
809
804
& self , hops : Vec < RouteHop > , probing_cookie_secret : [ u8 ; 32 ] , entropy_source : & ES ,
810
805
node_signer : & NS , best_block_height : u32 , send_payment_along_path : F
@@ -1396,8 +1391,8 @@ mod tests {
1396
1391
& Route { paths : vec ! [ ] , payment_params : None } , Some ( Retry :: Attempts ( 1 ) ) ,
1397
1392
Some ( expired_route_params. payment_params . clone ( ) ) , & & keys_manager, 0 ) . unwrap ( ) ;
1398
1393
outbound_payments. retry_payment_internal (
1399
- PaymentId ( [ 0 ; 32 ] ) , PaymentHash ( [ 0 ; 32 ] ) , expired_route_params, & & router, vec ! [ ] ,
1400
- & || InFlightHtlcs :: new ( ) , & & keys_manager, & & keys_manager, 0 , & & logger, & pending_events,
1394
+ PaymentId ( [ 0 ; 32 ] ) , expired_route_params, & & router, vec ! [ ] , & || InFlightHtlcs :: new ( ) ,
1395
+ & & keys_manager, & & keys_manager, 0 , & & logger, & pending_events,
1401
1396
& |_, _, _, _, _, _, _, _, _| Ok ( ( ) ) ) ;
1402
1397
let events = pending_events. lock ( ) . unwrap ( ) ;
1403
1398
assert_eq ! ( events. len( ) , 1 ) ;
@@ -1442,8 +1437,8 @@ mod tests {
1442
1437
& Route { paths : vec ! [ ] , payment_params : None } , Some ( Retry :: Attempts ( 1 ) ) ,
1443
1438
Some ( route_params. payment_params . clone ( ) ) , & & keys_manager, 0 ) . unwrap ( ) ;
1444
1439
outbound_payments. retry_payment_internal (
1445
- PaymentId ( [ 0 ; 32 ] ) , PaymentHash ( [ 0 ; 32 ] ) , route_params, & & router, vec ! [ ] ,
1446
- & || InFlightHtlcs :: new ( ) , & & keys_manager, & & keys_manager, 0 , & & logger, & pending_events,
1440
+ PaymentId ( [ 0 ; 32 ] ) , route_params, & & router, vec ! [ ] , & || InFlightHtlcs :: new ( ) ,
1441
+ & & keys_manager, & & keys_manager, 0 , & & logger, & pending_events,
1447
1442
& |_, _, _, _, _, _, _, _, _| Ok ( ( ) ) ) ;
1448
1443
let events = pending_events. lock ( ) . unwrap ( ) ;
1449
1444
assert_eq ! ( events. len( ) , 1 ) ;
0 commit comments