@@ -145,7 +145,7 @@ use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
145
145
use lightning:: ln:: channelmanager:: { ChannelDetails , PaymentId , PaymentSendFailure } ;
146
146
use lightning:: ln:: msgs:: LightningError ;
147
147
use lightning:: routing:: scoring:: { LockableScore , Score } ;
148
- use lightning:: routing:: router:: { PaymentParameters , Route , RouteParameters } ;
148
+ use lightning:: routing:: router:: { PaymentParameters , Route , RouteHop , RouteParameters } ;
149
149
use lightning:: util:: events:: { Event , EventHandler } ;
150
150
use lightning:: util:: logger:: Logger ;
151
151
use time_utils:: Time ;
@@ -159,6 +159,7 @@ use core::ops::Deref;
159
159
use core:: time:: Duration ;
160
160
#[ cfg( feature = "std" ) ]
161
161
use std:: time:: SystemTime ;
162
+ use lightning:: util:: errors:: APIError ;
162
163
163
164
/// A utility for paying [`Invoice`]s and sending spontaneous payments.
164
165
///
@@ -190,6 +191,7 @@ where
190
191
/// Caches the overall attempts at making a payment, which is updated prior to retrying.
191
192
payment_cache : Mutex < HashMap < PaymentHash , PaymentAttempts < T > > > ,
192
193
retry : Retry ,
194
+ total_inflight_map : Mutex < HashMap < ( u64 , bool ) , u64 > > ,
193
195
}
194
196
195
197
/// Storing minimal payment attempts information required for determining if a outbound payment can
@@ -321,6 +323,7 @@ where
321
323
event_handler,
322
324
payment_cache : Mutex :: new ( HashMap :: new ( ) ) ,
323
325
retry,
326
+ total_inflight_map : Mutex :: new ( HashMap :: new ( ) ) ,
324
327
}
325
328
}
326
329
@@ -431,7 +434,12 @@ where
431
434
) . map_err ( |e| PaymentError :: Routing ( e) ) ?;
432
435
433
436
match send_payment ( & route) {
434
- Ok ( payment_id) => Ok ( payment_id) ,
437
+ Ok ( payment_id) => {
438
+ for hops in route. paths {
439
+ self . process_path_inflight_htlcs ( & hops) ;
440
+ }
441
+ Ok ( payment_id)
442
+ } ,
435
443
Err ( e) => match e {
436
444
PaymentSendFailure :: ParameterError ( _) => Err ( e) ,
437
445
PaymentSendFailure :: PathParameterError ( _) => Err ( e) ,
@@ -446,7 +454,27 @@ where
446
454
Err ( e)
447
455
}
448
456
} ,
449
- PaymentSendFailure :: PartialFailure { failed_paths_retry, payment_id, .. } => {
457
+ PaymentSendFailure :: PartialFailure { failed_paths_retry, payment_id, results } => {
458
+ // The ones that are `Ok()`s + `MonitorUpdateFailed` in `results` need to be used to update the map
459
+ // get the index of them and run to `route` to get the values we need to care about.
460
+ for ( idx, result) in results. iter ( ) . enumerate ( ) {
461
+ match result {
462
+ Ok ( _) => {
463
+ if let Some ( failed_path) = route. paths . get ( idx) {
464
+ self . process_path_inflight_htlcs ( failed_path) ;
465
+ }
466
+ }
467
+ Err ( err) => match err {
468
+ APIError :: MonitorUpdateFailed => {
469
+ if let Some ( failed_path) = route. paths . get ( idx) {
470
+ self . process_path_inflight_htlcs ( failed_path) ;
471
+ }
472
+ }
473
+ _ => { }
474
+ }
475
+ }
476
+ }
477
+
450
478
if let Some ( retry_data) = failed_paths_retry {
451
479
// Some paths were sent, even if we failed to send the full MPP value our
452
480
// recipient may misbehave and claim the funds, at which point we have to
@@ -466,6 +494,29 @@ where
466
494
} . map_err ( |e| PaymentError :: Sending ( e) )
467
495
}
468
496
497
+ fn process_path_inflight_htlcs ( & self , hops : & Vec < RouteHop > ) {
498
+ // total_inflight_map needs to be direction-sensitive when keeping track of the HTLC value
499
+ // that is held up. However, the `hops` array, which is a path returned by `find_route` in
500
+ // the router excludes the payer node. In the following lines, the payer's information is
501
+ // hardcoded with an inflight value of 0 so that we can correctly represent the first hop
502
+ // in our sliding window of two.
503
+ let our_node_id: PublicKey = self . payer . node_id ( ) ;
504
+ let reversed_hops_with_payer = core:: iter:: once ( ( 0u64 , our_node_id) ) . chain (
505
+ hops. split_last ( ) . unwrap ( ) . 1 . iter ( ) . map ( |hop| ( hop. fee_msat , hop. pubkey ) ) ) . rev ( ) ;
506
+ let mut cumulative_msat = hops. last ( ) . unwrap ( ) . fee_msat ;
507
+
508
+ // Taking the reversed vector from above, we zip it with just the reversed hops list to
509
+ // work "backwards" of the given path, since the last hop's `fee_msat` actually represents
510
+ // the total amount sent.
511
+ for ( next_hop, prev_hop) in hops. iter ( ) . rev ( ) . zip ( reversed_hops_with_payer) {
512
+ cumulative_msat += prev_hop. 0 ;
513
+ self . total_inflight_map . lock ( ) . unwrap ( )
514
+ . entry ( ( next_hop. short_channel_id , next_hop. pubkey < prev_hop. 1 ) )
515
+ . and_modify ( |used_liquidity_msat| * used_liquidity_msat += cumulative_msat)
516
+ . or_insert ( cumulative_msat) ;
517
+ }
518
+ }
519
+
469
520
fn retry_payment (
470
521
& self , payment_id : PaymentId , payment_hash : PaymentHash , params : & RouteParameters
471
522
) -> Result < ( ) , ( ) > {
0 commit comments