use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError, MAX_VALUE_MSAT};
#[cfg(test)]
use crate::ln::outbound_payment;
-use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, Retry};
+use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment};
use crate::ln::wire::Encode;
use crate::chain::keysinterface::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, ChannelSigner};
use crate::util::config::{UserConfig, ChannelConfig};
use core::ops::Deref;
// Re-export this for use in the public API.
-pub use crate::ln::outbound_payment::PaymentSendFailure;
+pub use crate::ln::outbound_payment::{PaymentSendFailure, Retry};
// We hold various information about HTLC relay in the HTLC objects in Channel itself:
//
}
}
+/// Used by [`ChannelManager::list_recent_payments`] to express the status of recent payments.
+/// These include payments that have yet to find a successful path, or have unresolved HTLCs.
+#[derive(Debug, PartialEq)]
+pub enum RecentPaymentDetails {
+ /// When a payment is still being sent and awaiting successful delivery.
+ Pending {
+ /// Hash of the payment that is currently being sent but has yet to be fulfilled or
+ /// abandoned.
+ payment_hash: PaymentHash,
+ /// Total amount (in msat, excluding fees) across all paths for this payment,
+ /// not just the amount currently inflight.
+ total_msat: u64,
+ },
+ /// When a pending payment is fulfilled, we continue tracking it until all pending HTLCs have
+ /// been resolved. Upon receiving [`Event::PaymentSent`], we delay for a few minutes before the
+ /// payment is removed from tracking.
+ Fulfilled {
+ /// Hash of the payment that was claimed. `None` for serializations of [`ChannelManager`]
+ /// made before LDK version 0.0.104.
+ payment_hash: Option<PaymentHash>,
+ },
+ /// After a payment is explicitly abandoned by calling [`ChannelManager::abandon_payment`], it
+ /// is marked as abandoned until an [`Event::PaymentFailed`] is generated. A payment could also
+ /// be marked as abandoned if pathfinding fails repeatedly or retries have been exhausted.
+ Abandoned {
+ /// Hash of the payment that we have given up trying to send.
+ payment_hash: PaymentHash,
+ },
+}
+
/// Route hints used in constructing invoices for [phantom node payents].
///
/// [phantom node payments]: crate::chain::keysinterface::PhantomKeysManager
self.list_channels_with_filter(|&(_, ref channel)| channel.is_live())
}
+ /// Returns in an undefined order recent payments that -- if not fulfilled -- have yet to find a
+ /// successful path, or have unresolved HTLCs.
+ ///
+ /// This can be useful for payments that may have been prepared, but ultimately not sent, as a
+ /// result of a crash. If such a payment exists, is not listed here, and an
+ /// [`Event::PaymentSent`] has not been received, you may consider retrying the payment.
+ ///
+ /// [`Event::PaymentSent`]: events::Event::PaymentSent
+ pub fn list_recent_payments(&self) -> Vec<RecentPaymentDetails> {
+ self.pending_outbound_payments.pending_outbound_payments.lock().unwrap().iter()
+ .filter_map(|(_, pending_outbound_payment)| match pending_outbound_payment {
+ PendingOutboundPayment::Retryable { payment_hash, total_msat, .. } => {
+ Some(RecentPaymentDetails::Pending {
+ payment_hash: *payment_hash,
+ total_msat: *total_msat,
+ })
+ },
+ PendingOutboundPayment::Abandoned { payment_hash, .. } => {
+ Some(RecentPaymentDetails::Abandoned { payment_hash: *payment_hash })
+ },
+ PendingOutboundPayment::Fulfilled { payment_hash, .. } => {
+ Some(RecentPaymentDetails::Fulfilled { payment_hash: *payment_hash })
+ },
+ PendingOutboundPayment::Legacy { .. } => None
+ })
+ .collect()
+ }
+
/// Helper function that issues the channel close events
fn issue_channel_close_events(&self, channel: &Channel<<SP::Target as SignerProvider>::Signer>, closure_reason: ClosureReason) {
let mut pending_events_lock = self.pending_events.lock().unwrap();
/// Sends a payment along a given route.
///
- /// Value parameters are provided via the last hop in route, see documentation for RouteHop
+ /// Value parameters are provided via the last hop in route, see documentation for [`RouteHop`]
/// fields for more info.
///
+ /// May generate SendHTLCs message(s) event on success, which should be relayed (e.g. via
+ /// [`PeerManager::process_events`]).
+ ///
+ /// # Avoiding Duplicate Payments
+ ///
/// If a pending payment is currently in-flight with the same [`PaymentId`] provided, this
/// method will error with an [`APIError::InvalidRoute`]. Note, however, that once a payment
/// is no longer pending (either via [`ChannelManager::abandon_payment`], or handling of an
/// consider using the [`PaymentHash`] as the key for tracking payments. In that case, the
/// [`PaymentId`] should be a copy of the [`PaymentHash`] bytes.
///
- /// May generate SendHTLCs message(s) event on success, which should be relayed (e.g. via
- /// [`PeerManager::process_events`]).
+ /// Additionally, in the scenario where we begin the process of sending a payment, but crash
+ /// before `send_payment` returns (or prior to [`ChannelMonitorUpdate`] persistence if you're
+ /// using [`ChannelMonitorUpdateStatus::InProgress`]), the payment may be lost on restart. See
+ /// [`ChannelManager::list_recent_payments`] for more information.
+ ///
+ /// # Possible Error States on [`PaymentSendFailure`]
///
/// Each path may have a different return value, and PaymentSendValue may return a Vec with
/// each entry matching the corresponding-index entry in the route paths, see
- /// PaymentSendFailure for more info.
+ /// [`PaymentSendFailure`] for more info.
///
/// In general, a path may raise:
/// * [`APIError::InvalidRoute`] when an invalid route or forwarding parameter (cltv_delta, fee,
/// irrevocably committed to on our end. In such a case, do NOT retry the payment with a
/// different route unless you intend to pay twice!
///
- /// payment_secret is unrelated to payment_hash (or PaymentPreimage) and exists to authenticate
- /// the sender to the recipient and prevent payment-probing (deanonymization) attacks. For
- /// newer nodes, it will be provided to you in the invoice. If you do not have one, the Route
- /// must not contain multiple paths as multi-path payments require a recipient-provided
- /// payment_secret.
+ /// # A caution on `payment_secret`
///
- /// If a payment_secret *is* provided, we assume that the invoice had the payment_secret feature
- /// bit set (either as required or as available). If multiple paths are present in the Route,
- /// we assume the invoice had the basic_mpp feature set.
+ /// `payment_secret` is unrelated to `payment_hash` (or [`PaymentPreimage`]) and exists to
+ /// authenticate the sender to the recipient and prevent payment-probing (deanonymization)
+ /// attacks. For newer nodes, it will be provided to you in the invoice. If you do not have one,
+ /// the [`Route`] must not contain multiple paths as multi-path payments require a
+ /// recipient-provided `payment_secret`.
+ ///
+ /// If a `payment_secret` *is* provided, we assume that the invoice had the payment_secret
+ /// feature bit set (either as required or as available). If multiple paths are present in the
+ /// [`Route`], we assume the invoice had the basic_mpp feature set.
///
/// [`Event::PaymentSent`]: events::Event::PaymentSent
/// [`PeerManager::process_events`]: crate::ln::peer_handler::PeerManager::process_events
+ /// [`ChannelMonitorUpdateStatus::InProgress`]: crate::chain::ChannelMonitorUpdateStatus::InProgress
pub fn send_payment(&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, payment_id: PaymentId) -> Result<(), PaymentSendFailure> {
let best_block_height = self.best_block.read().unwrap().height();
self.pending_outbound_payments
/// [`send_payment`]: Self::send_payment
pub fn send_spontaneous_payment(&self, route: &Route, payment_preimage: Option<PaymentPreimage>, payment_id: PaymentId) -> Result<PaymentHash, PaymentSendFailure> {
let best_block_height = self.best_block.read().unwrap().height();
- self.pending_outbound_payments.send_spontaneous_payment(route, payment_preimage, payment_id, &self.entropy_source, &self.node_signer, best_block_height,
+ self.pending_outbound_payments.send_spontaneous_payment_with_route(
+ route, payment_preimage, payment_id, &self.entropy_source, &self.node_signer,
+ best_block_height,
+ |path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
+ self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
+ }
+
+ /// Similar to [`ChannelManager::send_spontaneous_payment`], but will automatically find a route
+ /// based on `route_params` and retry failed payment paths based on `retry_strategy`.
+ ///
+ /// See [`PaymentParameters::for_keysend`] for help in constructing `route_params` for spontaneous
+ /// payments.
+ ///
+ /// [`PaymentParameters::for_keysend`]: crate::routing::router::PaymentParameters::for_keysend
+ pub fn send_spontaneous_payment_with_retry(&self, payment_preimage: Option<PaymentPreimage>, payment_id: PaymentId, route_params: RouteParameters, retry_strategy: Retry) -> Result<PaymentHash, PaymentSendFailure> {
+ let best_block_height = self.best_block.read().unwrap().height();
+ self.pending_outbound_payments.send_spontaneous_payment(payment_preimage, payment_id,
+ retry_strategy, route_params, &self.router, self.list_usable_channels(),
+ self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer, best_block_height,
+ &self.logger,
|path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv|
self.send_payment_along_path(path, payment_params, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv))
}
events.into_inner()
}
+ #[cfg(feature = "_test_utils")]
+ pub fn push_pending_event(&self, event: events::Event) {
+ let mut events = self.pending_events.lock().unwrap();
+ events.push(event);
+ }
+
#[cfg(test)]
pub fn pop_pending_event(&self) -> Option<events::Event> {
let mut events = self.pending_events.lock().unwrap();
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
let peer_state = &mut *peer_state_lock;
for chan in peer_state.channel_by_id.values() {
- if let (Some(funding_txo), block_hash) = (chan.get_funding_txo(), chan.get_funding_tx_confirmed_in()) {
- res.push((funding_txo.txid, block_hash));
+ if let (Some(funding_txo), Some(block_hash)) = (chan.get_funding_txo(), chan.get_funding_tx_confirmed_in()) {
+ res.push((funding_txo.txid, Some(block_hash)));
}
}
}
session_privs: [session_priv_bytes].iter().map(|a| *a).collect(),
payment_hash: htlc.payment_hash,
payment_secret,
+ keysend_preimage: None, // only used for retries, and we'll never retry on startup
pending_amt_msat: path_amt,
pending_fee_msat: Some(path_fee),
total_msat: path_amt,