Skip to content

Commit

Permalink
Expose pending payments through ChannelManager
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jurvis committed Dec 5, 2022
1 parent 53eb0d7 commit 91cc02e
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 1 deletion.
38 changes: 38 additions & 0 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@ impl PendingOutboundPayment {
_ => false,
}
}

fn abandoned(&self) -> bool {
match self {
PendingOutboundPayment::Abandoned { .. } => true,
Expand Down Expand Up @@ -1203,6 +1204,25 @@ impl ChannelDetails {
}
}

/// We store payments that are pending resolution as `PendingOutboundPayment` in `ChannelManager`,
/// and they can take on either one of the following states as defined in this enum.
#[derive(Debug, PartialEq)]
pub enum PendingPaymentStatus {
Retryable,
Fulfilled,
Abandoned,
}

/// Details of a pending payment, as returned by [`ChannelManager::list_pending_payments`].
pub struct PendingPaymentDetails {
/// Current status of a payment that is pending resolution.
pub status: PendingPaymentStatus,
/// Total amount (in msat) across all paths for this payment, not just the amount currently
/// inflight. This field is `None` if the payment's status is either
/// `PendingPaymentStatus::Fulfilled` or `PendingPaymentPaymentStatus::Abandoned`.
pub total_msat: Option<u64>,
}

/// If a payment fails to send, it can be in one of several states. This enum is returned as the
/// Err() type describing which state the payment is in, see the description of individual enum
/// states for more.
Expand Down Expand Up @@ -1770,6 +1790,24 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
self.list_channels_with_filter(|&(_, ref channel)| channel.is_live())
}

/// Gets a list of payments in ramdom order that are currently pending resolution.
pub fn list_pending_payments(&self) -> Vec<PendingPaymentDetails> {
self.pending_outbound_payments.lock().unwrap().iter()
.filter_map(|(_, pending_outbound_payment)| match pending_outbound_payment {
PendingOutboundPayment::Retryable { total_msat, .. } => {
Some(PendingPaymentDetails { status: PendingPaymentStatus::Retryable, total_msat: Some(*total_msat) })
},
PendingOutboundPayment::Abandoned { .. } => {
Some(PendingPaymentDetails { status: PendingPaymentStatus::Abandoned, total_msat: None })
},
PendingOutboundPayment::Fulfilled { .. } => {
Some(PendingPaymentDetails { status: PendingPaymentStatus::Fulfilled, total_msat: None })
},
PendingOutboundPayment::Legacy { .. } => None
})
.collect()
}

/// Helper function that issues the channel close events
fn issue_channel_close_events(&self, channel: &Channel<<K::Target as KeysInterface>::Signer>, closure_reason: ClosureReason) {
let mut pending_events_lock = self.pending_events.lock().unwrap();
Expand Down
25 changes: 24 additions & 1 deletion lightning/src/ln/payment_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::chain::channelmonitor::{ANTI_REORG_DELAY, LATENCY_GRACE_PERIOD_BLOCKS
use crate::chain::transaction::OutPoint;
use crate::chain::keysinterface::KeysInterface;
use crate::ln::channel::EXPIRE_PREV_CONFIG_TICKS;
use crate::ln::channelmanager::{self, BREAKDOWN_TIMEOUT, ChannelManager, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PaymentSendFailure, IDEMPOTENCY_TIMEOUT_TICKS};
use crate::ln::channelmanager::{self, BREAKDOWN_TIMEOUT, ChannelManager, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PendingPaymentStatus, PaymentSendFailure, IDEMPOTENCY_TIMEOUT_TICKS};
use crate::ln::msgs;
use crate::ln::msgs::ChannelMessageHandler;
use crate::routing::router::{PaymentParameters, get_route};
Expand Down Expand Up @@ -1279,6 +1279,15 @@ fn test_trivial_inflight_htlc_tracking(){

assert_eq!(chan_1_used_liquidity, None);
assert_eq!(chan_2_used_liquidity, None);

let pending_payments = nodes[0].node.list_pending_payments();
assert_eq!(pending_payments.len(), 1);
assert_eq!(pending_payments[0].status, PendingPaymentStatus::Fulfilled);
}

// Remove fulfilled payment
for _ in 0..=IDEMPOTENCY_TIMEOUT_TICKS {
nodes[0].node.timer_tick_occurred();
}

// Send the payment, but do not claim it. Our inflight HTLCs should contain the pending payment.
Expand All @@ -1305,10 +1314,21 @@ fn test_trivial_inflight_htlc_tracking(){
// First hop accounts for expected 1000 msat fee
assert_eq!(chan_1_used_liquidity, Some(501000));
assert_eq!(chan_2_used_liquidity, Some(500000));

let pending_payments = nodes[0].node.list_pending_payments();
assert_eq!(pending_payments.len(), 1);
assert_eq!(pending_payments[0].status, PendingPaymentStatus::Retryable);
assert_eq!(pending_payments[0].total_msat, Some(500000));
}

// Now, let's claim the payment. This should result in the used liquidity to return `None`.
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage);

// Remove fulfilled payment
for _ in 0..=IDEMPOTENCY_TIMEOUT_TICKS {
nodes[0].node.timer_tick_occurred();
}

{
let inflight_htlcs = node_chanmgrs[0].compute_inflight_htlcs();

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

assert_eq!(chan_1_used_liquidity, None);
assert_eq!(chan_2_used_liquidity, None);

let pending_payments = nodes[0].node.list_pending_payments();
assert_eq!(pending_payments.len(), 0);
}
}

Expand Down

0 comments on commit 91cc02e

Please sign in to comment.