Skip to content

Commit ae6f582

Browse files
committed
Track currently inflight HTLCs
We add a HashMap to track currently inflight HTLCs as the liquidity they are using up for each channel.
1 parent 36bffb5 commit ae6f582

File tree

1 file changed

+54
-3
lines changed

1 file changed

+54
-3
lines changed

lightning-invoice/src/payment.rs

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
145145
use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
146146
use lightning::ln::msgs::LightningError;
147147
use lightning::routing::scoring::{LockableScore, Score};
148-
use lightning::routing::router::{PaymentParameters, Route, RouteParameters};
148+
use lightning::routing::router::{PaymentParameters, Route, RouteHop, RouteParameters};
149149
use lightning::util::events::{Event, EventHandler};
150150
use lightning::util::logger::Logger;
151151
use time_utils::Time;
@@ -159,6 +159,7 @@ use core::ops::Deref;
159159
use core::time::Duration;
160160
#[cfg(feature = "std")]
161161
use std::time::SystemTime;
162+
use lightning::util::errors::APIError;
162163

163164
/// A utility for paying [`Invoice`]s and sending spontaneous payments.
164165
///
@@ -190,6 +191,7 @@ where
190191
/// Caches the overall attempts at making a payment, which is updated prior to retrying.
191192
payment_cache: Mutex<HashMap<PaymentHash, PaymentAttempts<T>>>,
192193
retry: Retry,
194+
total_inflight_map: Mutex<HashMap<(u64, bool), u64>>,
193195
}
194196

195197
/// Storing minimal payment attempts information required for determining if a outbound payment can
@@ -321,6 +323,7 @@ where
321323
event_handler,
322324
payment_cache: Mutex::new(HashMap::new()),
323325
retry,
326+
total_inflight_map: Mutex::new(HashMap::new()),
324327
}
325328
}
326329

@@ -431,7 +434,12 @@ where
431434
).map_err(|e| PaymentError::Routing(e))?;
432435

433436
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+
},
435443
Err(e) => match e {
436444
PaymentSendFailure::ParameterError(_) => Err(e),
437445
PaymentSendFailure::PathParameterError(_) => Err(e),
@@ -446,7 +454,27 @@ where
446454
Err(e)
447455
}
448456
},
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+
450478
if let Some(retry_data) = failed_paths_retry {
451479
// Some paths were sent, even if we failed to send the full MPP value our
452480
// recipient may misbehave and claim the funds, at which point we have to
@@ -466,6 +494,29 @@ where
466494
}.map_err(|e| PaymentError::Sending(e))
467495
}
468496

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+
469520
fn retry_payment(
470521
&self, payment_id: PaymentId, payment_hash: PaymentHash, params: &RouteParameters
471522
) -> Result<(), ()> {

0 commit comments

Comments
 (0)