Skip to content

Commit 634e4fe

Browse files
committed
Expose pending payments through ChannelManager
Adds a new method, `list_recent_payments ` to `ChannelManager` that returns an array of `RecentPaymentDetails` containing the payment status (Fulfilled/Retryable/Abandoned) and its total amount across all paths.
1 parent e0a0add commit 634e4fe

File tree

2 files changed

+85
-3
lines changed

2 files changed

+85
-3
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,6 +1131,36 @@ impl ChannelDetails {
11311131
}
11321132
}
11331133

1134+
/// Used by [`ChannelManager::list_recent_payments`] to express the status of recent payments.
1135+
/// These include payments that have yet to find a successful path, or have unresolved HTLCs.
1136+
#[derive(Debug, PartialEq)]
1137+
pub enum RecentPaymentDetails {
1138+
/// When a payment is still being sent and awaiting successful delivery.
1139+
Pending {
1140+
/// Hash of the payment that is currently being sent but has yet to be fulfilled or
1141+
/// abandoned.
1142+
payment_hash: PaymentHash,
1143+
/// Total amount (in msat) across all paths for this payment, not just the amount currently
1144+
/// inflight.
1145+
total_msat: u64,
1146+
},
1147+
/// When a pending payment is fulfilled, we continue tracking it until all pending HTLCs have
1148+
/// been resolved. Upon receiving [`Event::PaymentSent`], we delay for a few minutes before the
1149+
/// payment is removed from tracking.
1150+
Fulfilled {
1151+
/// Hash of the payment that was claimed. `None` for serializations of [`ChannelManager`]
1152+
/// made before LDK version 0.0.104.
1153+
payment_hash: Option<PaymentHash>,
1154+
},
1155+
/// After a payment is explicitly abandoned by calling [`ChannelManager::abandon_payment`], it
1156+
/// is marked as abandoned until an [`Event::PaymentFailed`] is generated. A payment could also
1157+
/// be marked as abandoned if pathfinding fails repeatedly or retries have been exhausted.
1158+
Abandoned {
1159+
/// Hash of the payment that we have given up trying to send.
1160+
payment_hash: PaymentHash,
1161+
},
1162+
}
1163+
11341164
/// Route hints used in constructing invoices for [phantom node payents].
11351165
///
11361166
/// [phantom node payments]: crate::chain::keysinterface::PhantomKeysManager
@@ -1668,6 +1698,32 @@ where
16681698
self.list_channels_with_filter(|&(_, ref channel)| channel.is_live())
16691699
}
16701700

1701+
/// Returns in an undefined order recent payments that -- if not fulfilled -- have yet to find a
1702+
/// successful path, or have unresolved HTLCs.
1703+
///
1704+
/// This can be useful for figuring out whether or not a payment needs to be retried.
1705+
/// In general, if it is not listed here, you should consider retrying it if not
1706+
/// [`RecentPaymentDetails::Fulfilled`].
1707+
pub fn list_recent_payments(&self) -> Vec<RecentPaymentDetails> {
1708+
self.pending_outbound_payments.pending_outbound_payments.lock().unwrap().iter()
1709+
.filter_map(|(_, pending_outbound_payment)| match pending_outbound_payment {
1710+
PendingOutboundPayment::Retryable { payment_hash, total_msat, .. } => {
1711+
Some(RecentPaymentDetails::Pending {
1712+
payment_hash: *payment_hash,
1713+
total_msat: *total_msat,
1714+
})
1715+
},
1716+
PendingOutboundPayment::Abandoned { payment_hash, .. } => {
1717+
Some(RecentPaymentDetails::Abandoned { payment_hash: *payment_hash })
1718+
},
1719+
PendingOutboundPayment::Fulfilled { payment_hash, .. } => {
1720+
Some(RecentPaymentDetails::Fulfilled { payment_hash: *payment_hash })
1721+
},
1722+
PendingOutboundPayment::Legacy { .. } => None
1723+
})
1724+
.collect()
1725+
}
1726+
16711727
/// Helper function that issues the channel close events
16721728
fn issue_channel_close_events(&self, channel: &Channel<<SP::Target as SignerProvider>::Signer>, closure_reason: ClosureReason) {
16731729
let mut pending_events_lock = self.pending_events.lock().unwrap();

lightning/src/ln/payment_tests.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::chain::channelmonitor::{ANTI_REORG_DELAY, LATENCY_GRACE_PERIOD_BLOCKS
1616
use crate::chain::keysinterface::EntropySource;
1717
use crate::chain::transaction::OutPoint;
1818
use crate::ln::channel::EXPIRE_PREV_CONFIG_TICKS;
19-
use crate::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChannelManager, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PaymentSendFailure, IDEMPOTENCY_TIMEOUT_TICKS};
19+
use crate::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChannelManager, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PaymentSendFailure, IDEMPOTENCY_TIMEOUT_TICKS, RecentPaymentDetails};
2020
use crate::ln::msgs;
2121
use crate::ln::msgs::ChannelMessageHandler;
2222
use crate::routing::gossip::RoutingFees;
@@ -1267,7 +1267,11 @@ fn test_trivial_inflight_htlc_tracking(){
12671267
let (_, _, chan_2_id, _) = create_announced_chan_between_nodes(&nodes, 1, 2);
12681268

12691269
// Send and claim the payment. Inflight HTLCs should be empty.
1270-
send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 500000);
1270+
let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], 500000);
1271+
nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), PaymentId(payment_hash.0)).unwrap();
1272+
check_added_monitors!(nodes[0], 1);
1273+
pass_along_route(&nodes[0], &[&vec!(&nodes[1], &nodes[2])[..]], 500000, payment_hash, payment_secret);
1274+
claim_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], payment_preimage);
12711275
{
12721276
let inflight_htlcs = node_chanmgrs[0].compute_inflight_htlcs();
12731277

@@ -1291,10 +1295,19 @@ fn test_trivial_inflight_htlc_tracking(){
12911295

12921296
assert_eq!(chan_1_used_liquidity, None);
12931297
assert_eq!(chan_2_used_liquidity, None);
1298+
1299+
let pending_payments = nodes[0].node.list_recent_payments();
1300+
assert_eq!(pending_payments.len(), 1);
1301+
assert_eq!(pending_payments[0], RecentPaymentDetails::Fulfilled { payment_hash: Some(payment_hash) });
1302+
}
1303+
1304+
// Remove fulfilled payment
1305+
for _ in 0..=IDEMPOTENCY_TIMEOUT_TICKS {
1306+
nodes[0].node.timer_tick_occurred();
12941307
}
12951308

12961309
// Send the payment, but do not claim it. Our inflight HTLCs should contain the pending payment.
1297-
let (payment_preimage, _, _) = route_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 500000);
1310+
let (payment_preimage, payment_hash, _) = route_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 500000);
12981311
{
12991312
let inflight_htlcs = node_chanmgrs[0].compute_inflight_htlcs();
13001313

@@ -1319,10 +1332,20 @@ fn test_trivial_inflight_htlc_tracking(){
13191332
// First hop accounts for expected 1000 msat fee
13201333
assert_eq!(chan_1_used_liquidity, Some(501000));
13211334
assert_eq!(chan_2_used_liquidity, Some(500000));
1335+
1336+
let pending_payments = nodes[0].node.list_recent_payments();
1337+
assert_eq!(pending_payments.len(), 1);
1338+
assert_eq!(pending_payments[0], RecentPaymentDetails::Pending { payment_hash, total_msat: 500000 });
13221339
}
13231340

13241341
// Now, let's claim the payment. This should result in the used liquidity to return `None`.
13251342
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage);
1343+
1344+
// Remove fulfilled payment
1345+
for _ in 0..=IDEMPOTENCY_TIMEOUT_TICKS {
1346+
nodes[0].node.timer_tick_occurred();
1347+
}
1348+
13261349
{
13271350
let inflight_htlcs = node_chanmgrs[0].compute_inflight_htlcs();
13281351

@@ -1346,6 +1369,9 @@ fn test_trivial_inflight_htlc_tracking(){
13461369

13471370
assert_eq!(chan_1_used_liquidity, None);
13481371
assert_eq!(chan_2_used_liquidity, None);
1372+
1373+
let pending_payments = nodes[0].node.list_recent_payments();
1374+
assert_eq!(pending_payments.len(), 0);
13491375
}
13501376
}
13511377

0 commit comments

Comments
 (0)