Skip to content

Commit fa440fa

Browse files
committed
Remove inflight HTLC value from map on PaymentPathFailed/Successful
When processing a `PaymentPathSuccessful` event, the associated inflight HTLC value is subtracted from the map, `total_inflight_map`. On `PaymentPathFailed`, when a payment is abandoned, we subtract the previously-inflight value from any existing records in the map.
1 parent 7cef880 commit fa440fa

File tree

1 file changed

+59
-2
lines changed

1 file changed

+59
-2
lines changed

lightning-invoice/src/payment.rs

+59-2
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,23 @@ where
517517
}
518518
}
519519

520+
fn remove_path_inflight_htlcs(&self, hops: &Vec<RouteHop>) {
521+
// Here, just like `process_path_inflight_htlcs`, we are given a vector of hops that excludes
522+
// any information about the payer. However, in our `total_inflight_map`, we keep track
523+
// of possible inbound/outbound liquidity from the payer as well. Therefore, it is accounted
524+
// for by hard-coding the same tuple as in `process_path_inflight_htlcs`
525+
let our_node_id: PublicKey = self.payer.node_id();
526+
let hops_with_us = core::iter::once((0u64, our_node_id)).chain(hops.iter().map(|hop| (hop.fee_msat, hop.pubkey)));
527+
528+
for (next_hop, prev_hop) in hops.iter().zip(hops_with_us) {
529+
println!("Subtracting {} inflight msats from {}", next_hop.fee_msat, next_hop.short_channel_id);
530+
self.total_inflight_map.lock().unwrap()
531+
.entry((next_hop.short_channel_id, next_hop.pubkey < prev_hop.1))
532+
.and_modify(|used_liquidity_msat| *used_liquidity_msat = used_liquidity_msat.saturating_sub(next_hop.fee_msat) )
533+
.or_insert(0);
534+
}
535+
}
536+
520537
fn retry_payment(
521538
&self, payment_id: PaymentId, payment_hash: PaymentHash, params: &RouteParameters
522539
) -> Result<(), ()> {
@@ -613,22 +630,26 @@ where
613630
} else if *rejected_by_dest {
614631
log_trace!(self.logger, "Payment {} rejected by destination; not retrying", log_bytes!(payment_hash.0));
615632
self.payer.abandon_payment(payment_id.unwrap());
633+
self.remove_path_inflight_htlcs(path);
616634
} else if retry.is_none() {
617635
log_trace!(self.logger, "Payment {} missing retry params; not retrying", log_bytes!(payment_hash.0));
618636
self.payer.abandon_payment(payment_id.unwrap());
637+
self.remove_path_inflight_htlcs(path);
619638
} else if self.retry_payment(payment_id.unwrap(), *payment_hash, retry.as_ref().unwrap()).is_ok() {
620639
// We retried at least somewhat, don't provide the PaymentPathFailed event to the user.
621640
return;
622641
} else {
623642
self.payer.abandon_payment(payment_id.unwrap());
643+
self.remove_path_inflight_htlcs(path);
624644
}
625645
},
626646
Event::PaymentFailed { payment_hash, .. } => {
627647
self.remove_cached_payment(&payment_hash);
628648
},
629649
Event::PaymentPathSuccessful { path, .. } => {
630-
let path = path.iter().collect::<Vec<_>>();
631-
self.scorer.lock().payment_path_successful(&path);
650+
let path_with_refs = path.iter().collect::<Vec<_>>();
651+
self.scorer.lock().payment_path_successful(&path_with_refs);
652+
self.remove_path_inflight_htlcs(path);
632653
},
633654
Event::PaymentSent { payment_hash, .. } => {
634655
let mut payment_cache = self.payment_cache.lock().unwrap();
@@ -766,11 +787,31 @@ mod tests {
766787
let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
767788
assert_eq!(*payer.attempts.borrow(), 1);
768789

790+
let inflight_map = invoice_payer.total_inflight_map.lock().unwrap();
791+
// TestRouter returns two hops, with `fee_msat` divided by two. The invoice instantiated above
792+
// is hard-coded to test sending an invoice with a value of 128 msats.
793+
assert_eq!(inflight_map.get(&(0, true)).unwrap().clone(), 64);
794+
assert_eq!(inflight_map.get(&(1, true)).unwrap().clone(), 64);
795+
drop(inflight_map);
796+
769797
invoice_payer.handle_event(&Event::PaymentSent {
770798
payment_id, payment_preimage, payment_hash, fee_paid_msat: None
771799
});
772800
assert_eq!(*event_handled.borrow(), true);
773801
assert_eq!(*payer.attempts.borrow(), 1);
802+
803+
invoice_payer.handle_event(&Event::PaymentPathSuccessful {
804+
payment_id: payment_id.unwrap(), payment_hash: Some(payment_hash),
805+
path: TestRouter::route_for_value(final_value_msat).paths[0].clone()
806+
});
807+
invoice_payer.handle_event(&Event::PaymentPathSuccessful {
808+
payment_id: payment_id.unwrap(), payment_hash: Some(payment_hash),
809+
path: TestRouter::route_for_value(final_value_msat).paths[1].clone()
810+
});
811+
812+
let inflight_map = invoice_payer.total_inflight_map.lock().unwrap();
813+
assert_eq!(inflight_map.get(&(0, true)).unwrap().clone(), 0);
814+
assert_eq!(inflight_map.get(&(1, true)).unwrap().clone(), 0);
774815
}
775816

776817
#[test]
@@ -808,12 +849,28 @@ mod tests {
808849
invoice_payer.handle_event(&event);
809850
assert_eq!(*event_handled.borrow(), false);
810851
assert_eq!(*payer.attempts.borrow(), 2);
852+
let inflight_map = invoice_payer.total_inflight_map.lock().unwrap();
853+
assert_eq!(inflight_map.get(&(0, true)).unwrap().clone(), 64);
854+
assert_eq!(inflight_map.get(&(1, true)).unwrap().clone(), 64);
855+
drop(inflight_map);
811856

812857
invoice_payer.handle_event(&Event::PaymentSent {
813858
payment_id, payment_preimage, payment_hash, fee_paid_msat: None
814859
});
815860
assert_eq!(*event_handled.borrow(), true);
816861
assert_eq!(*payer.attempts.borrow(), 2);
862+
863+
invoice_payer.handle_event(&Event::PaymentPathSuccessful {
864+
payment_id: payment_id.unwrap(), payment_hash: Some(payment_hash),
865+
path: TestRouter::route_for_value(final_value_msat).paths[0].clone()
866+
});
867+
invoice_payer.handle_event(&Event::PaymentPathSuccessful {
868+
payment_id: payment_id.unwrap(), payment_hash: Some(payment_hash),
869+
path: TestRouter::route_for_value(final_value_msat).paths[1].clone()
870+
});
871+
let inflight_map = invoice_payer.total_inflight_map.lock().unwrap();
872+
assert_eq!(inflight_map.get(&(0, true)).unwrap().clone(), 0);
873+
assert_eq!(inflight_map.get(&(1, true)).unwrap().clone(), 0);
817874
}
818875

819876
#[test]

0 commit comments

Comments
 (0)