Skip to content

Commit c98f80d

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 2a72f4f commit c98f80d

File tree

2 files changed

+109
-15
lines changed

2 files changed

+109
-15
lines changed

lightning/src/ln/channelmanager.rs

+82-12
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,36 @@ impl ChannelDetails {
11541154
}
11551155
}
11561156

1157+
/// Used by [`ChannelManager::list_recent_payments`] to express the status of recent payments.
1158+
/// These include payments that have yet to find a successful path, or have unresolved HTLCs.
1159+
#[derive(Debug, PartialEq)]
1160+
pub enum RecentPaymentDetails {
1161+
/// When a payment is still being sent and awaiting successful delivery.
1162+
Pending {
1163+
/// Hash of the payment that is currently being sent but has yet to be fulfilled or
1164+
/// abandoned.
1165+
payment_hash: PaymentHash,
1166+
/// Total amount (in msat, excluding fees) across all paths for this payment,
1167+
/// not just the amount currently inflight.
1168+
total_msat: u64,
1169+
},
1170+
/// When a pending payment is fulfilled, we continue tracking it until all pending HTLCs have
1171+
/// been resolved. Upon receiving [`Event::PaymentSent`], we delay for a few minutes before the
1172+
/// payment is removed from tracking.
1173+
Fulfilled {
1174+
/// Hash of the payment that was claimed. `None` for serializations of [`ChannelManager`]
1175+
/// made before LDK version 0.0.104.
1176+
payment_hash: Option<PaymentHash>,
1177+
},
1178+
/// After a payment is explicitly abandoned by calling [`ChannelManager::abandon_payment`], it
1179+
/// is marked as abandoned until an [`Event::PaymentFailed`] is generated. A payment could also
1180+
/// be marked as abandoned if pathfinding fails repeatedly or retries have been exhausted.
1181+
Abandoned {
1182+
/// Hash of the payment that we have given up trying to send.
1183+
payment_hash: PaymentHash,
1184+
},
1185+
}
1186+
11571187
/// Route hints used in constructing invoices for [phantom node payents].
11581188
///
11591189
/// [phantom node payments]: crate::chain::keysinterface::PhantomKeysManager
@@ -1691,6 +1721,34 @@ where
16911721
self.list_channels_with_filter(|&(_, ref channel)| channel.is_live())
16921722
}
16931723

1724+
/// Returns in an undefined order recent payments that -- if not fulfilled -- have yet to find a
1725+
/// successful path, or have unresolved HTLCs.
1726+
///
1727+
/// This can be useful for payments that may have been prepared, but ultimately not sent, as a
1728+
/// result of a crash. If such a payment exists, is not listed here, and an
1729+
/// [`Event::PaymentSent`] has not been received, you may consider retrying the payment.
1730+
///
1731+
/// [`Event::PaymentSent`]: events::Event::PaymentSent
1732+
pub fn list_recent_payments(&self) -> Vec<RecentPaymentDetails> {
1733+
self.pending_outbound_payments.pending_outbound_payments.lock().unwrap().iter()
1734+
.filter_map(|(_, pending_outbound_payment)| match pending_outbound_payment {
1735+
PendingOutboundPayment::Retryable { payment_hash, total_msat, .. } => {
1736+
Some(RecentPaymentDetails::Pending {
1737+
payment_hash: *payment_hash,
1738+
total_msat: *total_msat,
1739+
})
1740+
},
1741+
PendingOutboundPayment::Abandoned { payment_hash, .. } => {
1742+
Some(RecentPaymentDetails::Abandoned { payment_hash: *payment_hash })
1743+
},
1744+
PendingOutboundPayment::Fulfilled { payment_hash, .. } => {
1745+
Some(RecentPaymentDetails::Fulfilled { payment_hash: *payment_hash })
1746+
},
1747+
PendingOutboundPayment::Legacy { .. } => None
1748+
})
1749+
.collect()
1750+
}
1751+
16941752
/// Helper function that issues the channel close events
16951753
fn issue_channel_close_events(&self, channel: &Channel<<SP::Target as SignerProvider>::Signer>, closure_reason: ClosureReason) {
16961754
let mut pending_events_lock = self.pending_events.lock().unwrap();
@@ -2415,9 +2473,14 @@ where
24152473

24162474
/// Sends a payment along a given route.
24172475
///
2418-
/// Value parameters are provided via the last hop in route, see documentation for RouteHop
2476+
/// Value parameters are provided via the last hop in route, see documentation for [`RouteHop`]
24192477
/// fields for more info.
24202478
///
2479+
/// May generate SendHTLCs message(s) event on success, which should be relayed (e.g. via
2480+
/// [`PeerManager::process_events`]).
2481+
///
2482+
/// # Avoiding Duplicate Payments
2483+
///
24212484
/// If a pending payment is currently in-flight with the same [`PaymentId`] provided, this
24222485
/// method will error with an [`APIError::InvalidRoute`]. Note, however, that once a payment
24232486
/// is no longer pending (either via [`ChannelManager::abandon_payment`], or handling of an
@@ -2430,12 +2493,16 @@ where
24302493
/// consider using the [`PaymentHash`] as the key for tracking payments. In that case, the
24312494
/// [`PaymentId`] should be a copy of the [`PaymentHash`] bytes.
24322495
///
2433-
/// May generate SendHTLCs message(s) event on success, which should be relayed (e.g. via
2434-
/// [`PeerManager::process_events`]).
2496+
/// Additionally, in the scenario where we begin the process of sending a payment, but crash
2497+
/// before `send_payment` returns (or prior to [`ChannelMonitorUpdate`] persistence if you're
2498+
/// using [`ChannelMonitorUpdateStatus::InProgress`]), the payment may be lost on restart. See
2499+
/// [`ChannelManager::list_recent_payments`] for more information.
2500+
///
2501+
/// # Possible Error States on [`PaymentSendFailure`]
24352502
///
24362503
/// Each path may have a different return value, and PaymentSendValue may return a Vec with
24372504
/// each entry matching the corresponding-index entry in the route paths, see
2438-
/// PaymentSendFailure for more info.
2505+
/// [`PaymentSendFailure`] for more info.
24392506
///
24402507
/// In general, a path may raise:
24412508
/// * [`APIError::InvalidRoute`] when an invalid route or forwarding parameter (cltv_delta, fee,
@@ -2450,18 +2517,21 @@ where
24502517
/// irrevocably committed to on our end. In such a case, do NOT retry the payment with a
24512518
/// different route unless you intend to pay twice!
24522519
///
2453-
/// payment_secret is unrelated to payment_hash (or PaymentPreimage) and exists to authenticate
2454-
/// the sender to the recipient and prevent payment-probing (deanonymization) attacks. For
2455-
/// newer nodes, it will be provided to you in the invoice. If you do not have one, the Route
2456-
/// must not contain multiple paths as multi-path payments require a recipient-provided
2457-
/// payment_secret.
2520+
/// # A caution on `payment_secret`
2521+
///
2522+
/// `payment_secret` is unrelated to `payment_hash` (or [`PaymentPreimage`]) and exists to
2523+
/// authenticate the sender to the recipient and prevent payment-probing (deanonymization)
2524+
/// attacks. For newer nodes, it will be provided to you in the invoice. If you do not have one,
2525+
/// the [`Route`] must not contain multiple paths as multi-path payments require a
2526+
/// recipient-provided `payment_secret`.
24582527
///
2459-
/// If a payment_secret *is* provided, we assume that the invoice had the payment_secret feature
2460-
/// bit set (either as required or as available). If multiple paths are present in the Route,
2461-
/// we assume the invoice had the basic_mpp feature set.
2528+
/// If a `payment_secret` *is* provided, we assume that the invoice had the payment_secret
2529+
/// feature bit set (either as required or as available). If multiple paths are present in the
2530+
/// [`Route`], we assume the invoice had the basic_mpp feature set.
24622531
///
24632532
/// [`Event::PaymentSent`]: events::Event::PaymentSent
24642533
/// [`PeerManager::process_events`]: crate::ln::peer_handler::PeerManager::process_events
2534+
/// [`ChannelMonitorUpdateStatus::InProgress`]: crate::chain::ChannelMonitorUpdateStatus::InProgress
24652535
pub fn send_payment(&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, payment_id: PaymentId) -> Result<(), PaymentSendFailure> {
24662536
let best_block_height = self.best_block.read().unwrap().height();
24672537
self.pending_outbound_payments

lightning/src/ln/payment_tests.rs

+27-3
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::features::InvoiceFeatures;
2121
use crate::ln::msgs;
2222
use crate::ln::msgs::ChannelMessageHandler;
@@ -1274,7 +1274,11 @@ fn test_trivial_inflight_htlc_tracking(){
12741274
let (_, _, chan_2_id, _) = create_announced_chan_between_nodes(&nodes, 1, 2);
12751275

12761276
// Send and claim the payment. Inflight HTLCs should be empty.
1277-
send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 500000);
1277+
let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], 500000);
1278+
nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), PaymentId(payment_hash.0)).unwrap();
1279+
check_added_monitors!(nodes[0], 1);
1280+
pass_along_route(&nodes[0], &[&vec!(&nodes[1], &nodes[2])[..]], 500000, payment_hash, payment_secret);
1281+
claim_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], payment_preimage);
12781282
{
12791283
let inflight_htlcs = node_chanmgrs[0].compute_inflight_htlcs();
12801284

@@ -1299,9 +1303,17 @@ fn test_trivial_inflight_htlc_tracking(){
12991303
assert_eq!(chan_1_used_liquidity, None);
13001304
assert_eq!(chan_2_used_liquidity, None);
13011305
}
1306+
let pending_payments = nodes[0].node.list_recent_payments();
1307+
assert_eq!(pending_payments.len(), 1);
1308+
assert_eq!(pending_payments[0], RecentPaymentDetails::Fulfilled { payment_hash: Some(payment_hash) });
1309+
1310+
// Remove fulfilled payment
1311+
for _ in 0..=IDEMPOTENCY_TIMEOUT_TICKS {
1312+
nodes[0].node.timer_tick_occurred();
1313+
}
13021314

13031315
// Send the payment, but do not claim it. Our inflight HTLCs should contain the pending payment.
1304-
let (payment_preimage, _, _) = route_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 500000);
1316+
let (payment_preimage, payment_hash, _) = route_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 500000);
13051317
{
13061318
let inflight_htlcs = node_chanmgrs[0].compute_inflight_htlcs();
13071319

@@ -1327,9 +1339,18 @@ fn test_trivial_inflight_htlc_tracking(){
13271339
assert_eq!(chan_1_used_liquidity, Some(501000));
13281340
assert_eq!(chan_2_used_liquidity, Some(500000));
13291341
}
1342+
let pending_payments = nodes[0].node.list_recent_payments();
1343+
assert_eq!(pending_payments.len(), 1);
1344+
assert_eq!(pending_payments[0], RecentPaymentDetails::Pending { payment_hash, total_msat: 500000 });
13301345

13311346
// Now, let's claim the payment. This should result in the used liquidity to return `None`.
13321347
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage);
1348+
1349+
// Remove fulfilled payment
1350+
for _ in 0..=IDEMPOTENCY_TIMEOUT_TICKS {
1351+
nodes[0].node.timer_tick_occurred();
1352+
}
1353+
13331354
{
13341355
let inflight_htlcs = node_chanmgrs[0].compute_inflight_htlcs();
13351356

@@ -1354,6 +1375,9 @@ fn test_trivial_inflight_htlc_tracking(){
13541375
assert_eq!(chan_1_used_liquidity, None);
13551376
assert_eq!(chan_2_used_liquidity, None);
13561377
}
1378+
1379+
let pending_payments = nodes[0].node.list_recent_payments();
1380+
assert_eq!(pending_payments.len(), 0);
13571381
}
13581382

13591383
#[test]

0 commit comments

Comments
 (0)