Skip to content

Commit aff6659

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

File tree

2 files changed

+73
-1
lines changed

2 files changed

+73
-1
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,7 @@ impl PendingOutboundPayment {
485485
_ => false,
486486
}
487487
}
488+
488489
fn abandoned(&self) -> bool {
489490
match self {
490491
PendingOutboundPayment::Abandoned { .. } => true,
@@ -1203,6 +1204,36 @@ impl ChannelDetails {
12031204
}
12041205
}
12051206

1207+
/// We store payments that are pending resolution as `PendingOutboundPayment` in `ChannelManager`,
1208+
/// and they can take on either one of the following states as defined in this enum.
1209+
#[derive(Debug, PartialEq)]
1210+
pub enum PendingPaymentStatus {
1211+
/// When a payment is still being sent and awaiting successful delivery.
1212+
Retryable,
1213+
/// When a pending payment is fulfilled, we continue tracking it until all pending HTLCs have
1214+
/// been resolved. This ensures we don't look up pending payments in ChannelMonitors on restart
1215+
/// and add a pending payment that was already fulfilled.
1216+
Fulfilled,
1217+
/// When a payer gives up trying to retry a payment, they inform us, letting us generate a
1218+
/// `PaymentFailed` event when all HTLCs have irrevocably failed. This avoids a number of race
1219+
/// conditions in MPP-aware payment retriers (1), where the possibility of multiple
1220+
/// `PaymentPathFailed` events with `all_paths_failed` can be pending at once, confusing a
1221+
/// downstream event handler as to when a payment has actually failed.
1222+
///
1223+
/// (1) https://github.com/lightningdevkit/rust-lightning/issues/1164
1224+
Abandoned,
1225+
}
1226+
1227+
/// Details of a pending payment, as returned by [`ChannelManager::list_pending_payments`].
1228+
pub struct PendingPaymentDetails {
1229+
/// Current status of a payment that is pending resolution.
1230+
pub status: PendingPaymentStatus,
1231+
/// Total amount (in msat) across all paths for this payment, not just the amount currently
1232+
/// inflight. This field is `None` if the payment's status is either
1233+
/// `PendingPaymentStatus::Fulfilled` or `PendingPaymentPaymentStatus::Abandoned`.
1234+
pub total_msat: Option<u64>,
1235+
}
1236+
12061237
/// If a payment fails to send, it can be in one of several states. This enum is returned as the
12071238
/// Err() type describing which state the payment is in, see the description of individual enum
12081239
/// states for more.
@@ -1770,6 +1801,24 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
17701801
self.list_channels_with_filter(|&(_, ref channel)| channel.is_live())
17711802
}
17721803

1804+
/// Gets a list of payments in ramdom order that are currently pending resolution.
1805+
pub fn list_pending_payments(&self) -> Vec<PendingPaymentDetails> {
1806+
self.pending_outbound_payments.lock().unwrap().iter()
1807+
.filter_map(|(_, pending_outbound_payment)| match pending_outbound_payment {
1808+
PendingOutboundPayment::Retryable { total_msat, .. } => {
1809+
Some(PendingPaymentDetails { status: PendingPaymentStatus::Retryable, total_msat: Some(*total_msat) })
1810+
},
1811+
PendingOutboundPayment::Abandoned { .. } => {
1812+
Some(PendingPaymentDetails { status: PendingPaymentStatus::Abandoned, total_msat: None })
1813+
},
1814+
PendingOutboundPayment::Fulfilled { .. } => {
1815+
Some(PendingPaymentDetails { status: PendingPaymentStatus::Fulfilled, total_msat: None })
1816+
},
1817+
PendingOutboundPayment::Legacy { .. } => None
1818+
})
1819+
.collect()
1820+
}
1821+
17731822
/// Helper function that issues the channel close events
17741823
fn issue_channel_close_events(&self, channel: &Channel<<K::Target as KeysInterface>::Signer>, closure_reason: ClosureReason) {
17751824
let mut pending_events_lock = self.pending_events.lock().unwrap();

lightning/src/ln/payment_tests.rs

Lines changed: 24 additions & 1 deletion
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::transaction::OutPoint;
1717
use crate::chain::keysinterface::KeysInterface;
1818
use crate::ln::channel::EXPIRE_PREV_CONFIG_TICKS;
19-
use crate::ln::channelmanager::{self, BREAKDOWN_TIMEOUT, ChannelManager, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PaymentSendFailure, IDEMPOTENCY_TIMEOUT_TICKS};
19+
use crate::ln::channelmanager::{self, BREAKDOWN_TIMEOUT, ChannelManager, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PendingPaymentStatus, PaymentSendFailure, IDEMPOTENCY_TIMEOUT_TICKS};
2020
use crate::ln::msgs;
2121
use crate::ln::msgs::ChannelMessageHandler;
2222
use crate::routing::router::{PaymentParameters, get_route};
@@ -1279,6 +1279,15 @@ fn test_trivial_inflight_htlc_tracking(){
12791279

12801280
assert_eq!(chan_1_used_liquidity, None);
12811281
assert_eq!(chan_2_used_liquidity, None);
1282+
1283+
let pending_payments = nodes[0].node.list_pending_payments();
1284+
assert_eq!(pending_payments.len(), 1);
1285+
assert_eq!(pending_payments[0].status, PendingPaymentStatus::Fulfilled);
1286+
}
1287+
1288+
// Remove fulfilled payment
1289+
for _ in 0..=IDEMPOTENCY_TIMEOUT_TICKS {
1290+
nodes[0].node.timer_tick_occurred();
12821291
}
12831292

12841293
// Send the payment, but do not claim it. Our inflight HTLCs should contain the pending payment.
@@ -1305,10 +1314,21 @@ fn test_trivial_inflight_htlc_tracking(){
13051314
// First hop accounts for expected 1000 msat fee
13061315
assert_eq!(chan_1_used_liquidity, Some(501000));
13071316
assert_eq!(chan_2_used_liquidity, Some(500000));
1317+
1318+
let pending_payments = nodes[0].node.list_pending_payments();
1319+
assert_eq!(pending_payments.len(), 1);
1320+
assert_eq!(pending_payments[0].status, PendingPaymentStatus::Retryable);
1321+
assert_eq!(pending_payments[0].total_msat, Some(500000));
13081322
}
13091323

13101324
// Now, let's claim the payment. This should result in the used liquidity to return `None`.
13111325
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage);
1326+
1327+
// Remove fulfilled payment
1328+
for _ in 0..=IDEMPOTENCY_TIMEOUT_TICKS {
1329+
nodes[0].node.timer_tick_occurred();
1330+
}
1331+
13121332
{
13131333
let inflight_htlcs = node_chanmgrs[0].compute_inflight_htlcs();
13141334

@@ -1330,6 +1350,9 @@ fn test_trivial_inflight_htlc_tracking(){
13301350

13311351
assert_eq!(chan_1_used_liquidity, None);
13321352
assert_eq!(chan_2_used_liquidity, None);
1353+
1354+
let pending_payments = nodes[0].node.list_pending_payments();
1355+
assert_eq!(pending_payments.len(), 0);
13331356
}
13341357
}
13351358

0 commit comments

Comments
 (0)