use crate::chain::keysinterface::{EntropySource, NodeSigner, Recipient};
use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
-use crate::ln::channelmanager::{ChannelDetails, HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, MIN_HTLC_RELAY_HOLDING_CELL_MILLIS, PaymentId};
+use crate::ln::channelmanager::{ChannelDetails, HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, PaymentId};
use crate::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA as LDK_DEFAULT_MIN_FINAL_CLTV_EXPIRY_DELTA;
use crate::ln::msgs::DecodeError;
use crate::ln::onion_utils::HTLCFailReason;
use core::cmp;
use core::fmt::{self, Display, Formatter};
use core::ops::Deref;
-use core::time::Duration;
use crate::prelude::*;
use crate::sync::Mutex;
});
}
+ // Returns a bool indicating whether a PendingHTLCsForwardable event should be generated.
pub(super) fn fail_htlc<L: Deref>(
&self, source: &HTLCSource, payment_hash: &PaymentHash, onion_error: &HTLCFailReason,
path: &Vec<RouteHop>, session_priv: &SecretKey, payment_id: &PaymentId,
payment_params: &Option<PaymentParameters>, probing_cookie_secret: [u8; 32],
secp_ctx: &Secp256k1<secp256k1::All>, pending_events: &Mutex<Vec<events::Event>>, logger: &L
- ) where L::Target: Logger {
+ ) -> bool where L::Target: Logger {
#[cfg(test)]
let (network_update, short_channel_id, payment_retryable, onion_error_code, onion_error_data) = onion_error.decode_onion_failure(secp_ctx, logger, &source);
#[cfg(not(test))]
let mut session_priv_bytes = [0; 32];
session_priv_bytes.copy_from_slice(&session_priv[..]);
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
+
+ // If any payments already need retry, there's no need to generate a redundant
+ // `PendingHTLCsForwardable`.
+ let already_awaiting_retry = outbounds.iter().any(|(_, pmt)| {
+ let mut awaiting_retry = false;
+ if pmt.is_auto_retryable_now() {
+ if let PendingOutboundPayment::Retryable { pending_amt_msat, total_msat, .. } = pmt {
+ if pending_amt_msat < total_msat {
+ awaiting_retry = true;
+ }
+ }
+ }
+ awaiting_retry
+ });
+
let mut all_paths_failed = false;
let mut full_failure_ev = None;
- let mut pending_retry_ev = None;
+ let mut pending_retry_ev = false;
let mut retry = None;
let attempts_remaining = if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(*payment_id) {
if !payment.get_mut().remove(&session_priv_bytes, Some(&path)) {
log_trace!(logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
- return
+ return false
}
if payment.get().is_fulfilled() {
log_trace!(logger, "Received failure of HTLC with payment_hash {} after payment completion", log_bytes!(payment_hash.0));
- return
+ return false
}
let mut is_retryable_now = payment.get().is_auto_retryable_now();
if let Some(scid) = short_channel_id {
is_retryable_now
} else {
log_trace!(logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
- return
+ return false
};
core::mem::drop(outbounds);
log_trace!(logger, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0));
}
// If we miss abandoning the payment above, we *must* generate an event here or else the
// payment will sit in our outbounds forever.
- if attempts_remaining {
+ if attempts_remaining && !already_awaiting_retry {
debug_assert!(full_failure_ev.is_none());
- pending_retry_ev = Some(events::Event::PendingHTLCsForwardable {
- time_forwardable: Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS),
- });
+ pending_retry_ev = true;
}
events::Event::PaymentPathFailed {
payment_id: Some(*payment_id),
let mut pending_events = pending_events.lock().unwrap();
pending_events.push(path_failure);
if let Some(ev) = full_failure_ev { pending_events.push(ev); }
- if let Some(ev) = pending_retry_ev { pending_events.push(ev); }
+ pending_retry_ev
}
pub(super) fn abandon_payment(
#[cfg(test)]
mod tests {
- use bitcoin::blockdata::constants::genesis_block;
use bitcoin::network::constants::Network;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
fn do_fails_paying_after_expiration(on_retry: bool) {
let outbound_payments = OutboundPayments::new();
let logger = test_utils::TestLogger::new();
- let genesis_hash = genesis_block(Network::Testnet).header.block_hash();
- let network_graph = Arc::new(NetworkGraph::new(genesis_hash, &logger));
+ let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
let scorer = Mutex::new(test_utils::TestScorer::new());
let router = test_utils::TestRouter::new(network_graph, &scorer);
let secp_ctx = Secp256k1::new();
fn do_find_route_error(on_retry: bool) {
let outbound_payments = OutboundPayments::new();
let logger = test_utils::TestLogger::new();
- let genesis_hash = genesis_block(Network::Testnet).header.block_hash();
- let network_graph = Arc::new(NetworkGraph::new(genesis_hash, &logger));
+ let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
let scorer = Mutex::new(test_utils::TestScorer::new());
let router = test_utils::TestRouter::new(network_graph, &scorer);
let secp_ctx = Secp256k1::new();