Generate PendingHTLCsForwardable upon retryable payment
authorValentine Wallace <vwallace@protonmail.com>
Fri, 6 Jan 2023 23:39:51 +0000 (18:39 -0500)
committerValentine Wallace <vwallace@protonmail.com>
Wed, 25 Jan 2023 19:44:07 +0000 (14:44 -0500)
lightning/src/ln/channelmanager.rs
lightning/src/ln/outbound_payment.rs

index b18ddaeba6bc4d2a3b882f8993162418c9929913..9cf58c918256cf02b707ac83660d1a510c86979f 100644 (file)
@@ -388,7 +388,7 @@ impl MsgHandleErrInternal {
 /// Event::PendingHTLCsForwardable for the API guidelines indicating how long should be waited).
 /// This provides some limited amount of privacy. Ideally this would range from somewhere like one
 /// second to 30 seconds, but people expect lightning to be, you know, kinda fast, sadly.
-const MIN_HTLC_RELAY_HOLDING_CELL_MILLIS: u64 = 100;
+pub(super) const MIN_HTLC_RELAY_HOLDING_CELL_MILLIS: u64 = 100;
 
 /// For events which result in both a RevokeAndACK and a CommitmentUpdate, by default they should
 /// be sent in the order they appear in the return value, however sometimes the order needs to be
index 0a5a964b33b54a0da359fb1a698e886476602dc6..a971ea8b7f0131f8929c031c48798529f8e16fd3 100644 (file)
@@ -15,7 +15,7 @@ use bitcoin::secp256k1::{self, Secp256k1, SecretKey};
 
 use crate::chain::keysinterface::{EntropySource, NodeSigner, Recipient};
 use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
-use crate::ln::channelmanager::{HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, PaymentId};
+use crate::ln::channelmanager::{HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, MIN_HTLC_RELAY_HOLDING_CELL_MILLIS, PaymentId};
 use crate::ln::msgs::DecodeError;
 use crate::ln::onion_utils::HTLCFailReason;
 use crate::routing::router::{PaymentParameters, Route, RouteHop, RouteParameters, RoutePath};
@@ -29,6 +29,7 @@ use crate::util::time::tests::SinceEpoch;
 use core::cmp;
 use core::fmt::{self, Display, Formatter};
 use core::ops::Deref;
+use core::time::Duration;
 
 use crate::prelude::*;
 use crate::sync::Mutex;
@@ -87,6 +88,11 @@ impl PendingOutboundPayment {
                }
                false
        }
+       pub fn insert_previously_failed_scid(&mut self, scid: u64) {
+               if let PendingOutboundPayment::Retryable { route_params: Some(params), .. } = self {
+                       params.payment_params.previously_failed_channels.push(scid);
+               }
+       }
        pub(super) fn is_fulfilled(&self) -> bool {
                match self {
                        PendingOutboundPayment::Fulfilled { .. } => true,
@@ -793,7 +799,8 @@ impl OutboundPayments {
                let mut outbounds = self.pending_outbound_payments.lock().unwrap();
                let mut all_paths_failed = false;
                let mut full_failure_ev = None;
-               if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(*payment_id) {
+               let mut pending_retry_ev = 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
@@ -802,6 +809,10 @@ impl OutboundPayments {
                                log_trace!(logger, "Received failure of HTLC with payment_hash {} after payment completion", log_bytes!(payment_hash.0));
                                return
                        }
+                       let is_retryable_now = payment.get().is_retryable_now();
+                       if let Some(scid) = short_channel_id {
+                               payment.get_mut().insert_previously_failed_scid(scid);
+                       }
                        if payment.get().remaining_parts() == 0 {
                                all_paths_failed = true;
                                if payment.get().abandoned() {
@@ -812,10 +823,11 @@ impl OutboundPayments {
                                        payment.remove();
                                }
                        }
+                       is_retryable_now
                } else {
                        log_trace!(logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
                        return
-               }
+               };
                core::mem::drop(outbounds);
                let mut retry = if let Some(payment_params_data) = payment_params {
                        let path_last_hop = path.last().expect("Outbound payments must have had a valid path");
@@ -850,6 +862,12 @@ impl OutboundPayments {
                                if let Some(scid) = short_channel_id {
                                        retry.as_mut().map(|r| r.payment_params.previously_failed_channels.push(scid));
                                }
+                               if payment_retryable && attempts_remaining && retry.is_some() {
+                                       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),
+                                       });
+                               }
                                events::Event::PaymentPathFailed {
                                        payment_id: Some(*payment_id),
                                        payment_hash: payment_hash.clone(),
@@ -869,6 +887,7 @@ impl OutboundPayments {
                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); }
        }
 
        pub(super) fn abandon_payment(&self, payment_id: PaymentId) -> Option<events::Event> {