Stop timing out payments automatically, requiring abandon_payment
authorMatt Corallo <git@bluematt.me>
Sat, 8 Oct 2022 19:54:43 +0000 (19:54 +0000)
committerMatt Corallo <git@bluematt.me>
Wed, 2 Nov 2022 01:09:07 +0000 (01:09 +0000)
When the `abandon_payment` flow was added there was some concern
that upgrading users may not migrate to the new flow, causing
memory leaks in the pending-payment tracking.

While this is true, now that we're relying on the
pending_outbound_payments map for `send_payment` idempotency, the
risk of removing a payment prematurely goes up from "spurious
retry failure" to "sending a duplicative payment", which is much
worse.

Thus, we simply remove the automated payment timeout here,
explicitly requiring that users call `abandon_payment` when they
give up retrying a payment.

lightning/src/ln/channelmanager.rs
lightning/src/ln/functional_tests.rs
lightning/src/util/events.rs

index 5a98289c528b2a86dad6e0657e40bf88aeea356e..2e7e0d3c39840d809bd5fec6fe4597ee0ca424c5 100644 (file)
@@ -954,10 +954,6 @@ const CHECK_CLTV_EXPIRY_SANITY: u32 = MIN_CLTV_EXPIRY_DELTA as u32 - LATENCY_GRA
 #[allow(dead_code)]
 const CHECK_CLTV_EXPIRY_SANITY_2: u32 = MIN_CLTV_EXPIRY_DELTA as u32 - LATENCY_GRACE_PERIOD_BLOCKS - 2*CLTV_CLAIM_BUFFER;
 
-/// The number of blocks before we consider an outbound payment for expiry if it doesn't have any
-/// pending HTLCs in flight.
-pub(crate) const PAYMENT_EXPIRY_BLOCKS: u32 = 3;
-
 /// The number of ticks of [`ChannelManager::timer_tick_occurred`] until expiry of incomplete MPPs
 pub(crate) const MPP_TIMEOUT_TICKS: u8 = 3;
 
@@ -5833,21 +5829,6 @@ where
                payment_secrets.retain(|_, inbound_payment| {
                        inbound_payment.expiry_time > header.time as u64
                });
-
-               let mut outbounds = self.pending_outbound_payments.lock().unwrap();
-               let mut pending_events = self.pending_events.lock().unwrap();
-               outbounds.retain(|payment_id, payment| {
-                       if payment.remaining_parts() != 0 { return true }
-                       if let PendingOutboundPayment::Retryable { starting_block_height, payment_hash, .. } = payment {
-                               if *starting_block_height + PAYMENT_EXPIRY_BLOCKS <= height {
-                                       log_info!(self.logger, "Timing out payment with id {} and hash {}", log_bytes!(payment_id.0), log_bytes!(payment_hash.0));
-                                       pending_events.push(events::Event::PaymentFailed {
-                                               payment_id: *payment_id, payment_hash: *payment_hash,
-                                       });
-                                       false
-                               } else { true }
-                       } else { true }
-               });
        }
 
        fn get_relevant_txids(&self) -> Vec<Txid> {
index 30120225bd25a78de6e00f1f4783d75d0ee05d97..0709b64fcba88509ac3d288749809840d252cb19 100644 (file)
@@ -20,7 +20,7 @@ use crate::chain::transaction::OutPoint;
 use crate::chain::keysinterface::{BaseSign, KeysInterface};
 use crate::ln::{PaymentPreimage, PaymentSecret, PaymentHash};
 use crate::ln::channel::{commitment_tx_base_weight, COMMITMENT_TX_WEIGHT_PER_HTLC, CONCURRENT_INBOUND_HTLC_FEE_BUFFER, FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MIN_AFFORDABLE_HTLC_COUNT};
-use crate::ln::channelmanager::{self, ChannelManager, ChannelManagerReadArgs, PaymentId, RAACommitmentOrder, PaymentSendFailure, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA, PAYMENT_EXPIRY_BLOCKS};
+use crate::ln::channelmanager::{self, ChannelManager, ChannelManagerReadArgs, PaymentId, RAACommitmentOrder, PaymentSendFailure, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA};
 use crate::ln::channel::{Channel, ChannelError};
 use crate::ln::{chan_utils, onion_utils};
 use crate::ln::chan_utils::{OFFERED_HTLC_SCRIPT_WEIGHT, htlc_success_tx_weight, htlc_timeout_tx_weight, HTLCOutputInCommitment};
@@ -3184,10 +3184,9 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool, use
        mine_transaction(&nodes[1], &revoked_local_txn[0]);
        check_added_monitors!(nodes[1], 1);
        connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
-       assert!(ANTI_REORG_DELAY > PAYMENT_EXPIRY_BLOCKS); // We assume payments will also expire
 
        let events = nodes[1].node.get_and_clear_pending_events();
-       assert_eq!(events.len(), if deliver_bs_raa { 2 + (nodes.len() - 1) } else { 4 + nodes.len() });
+       assert_eq!(events.len(), if deliver_bs_raa { 2 + nodes.len() - 1 } else { 3 + nodes.len() });
        match events[0] {
                Event::ChannelClosed { reason: ClosureReason::CommitmentTxConfirmed, .. } => { },
                _ => panic!("Unexepected event"),
@@ -3200,15 +3199,18 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool, use
        }
        if !deliver_bs_raa {
                match events[2] {
+                       Event::PendingHTLCsForwardable { .. } => { },
+                       _ => panic!("Unexpected event"),
+               };
+               nodes[1].node.abandon_payment(PaymentId(fourth_payment_hash.0));
+               let payment_failed_events = nodes[1].node.get_and_clear_pending_events();
+               assert_eq!(payment_failed_events.len(), 1);
+               match payment_failed_events[0] {
                        Event::PaymentFailed { ref payment_hash, .. } => {
                                assert_eq!(*payment_hash, fourth_payment_hash);
                        },
                        _ => panic!("Unexpected event"),
                }
-               match events[3] {
-                       Event::PendingHTLCsForwardable { .. } => { },
-                       _ => panic!("Unexpected event"),
-               };
        }
        nodes[1].node.process_pending_htlc_forwards();
        check_added_monitors!(nodes[1], 1);
@@ -4325,14 +4327,7 @@ fn do_test_holding_cell_htlc_add_timeouts(forwarded_htlc: bool) {
                }
                expect_payment_failed_with_update!(nodes[0], second_payment_hash, false, chan_2.0.contents.short_channel_id, false);
        } else {
-               let events = nodes[1].node.get_and_clear_pending_events();
-               assert_eq!(events.len(), 2);
-               if let Event::PaymentPathFailed { ref payment_hash, .. } = events[0] {
-                       assert_eq!(*payment_hash, second_payment_hash);
-               } else { panic!("Unexpected event"); }
-               if let Event::PaymentFailed { ref payment_hash, .. } = events[1] {
-                       assert_eq!(*payment_hash, second_payment_hash);
-               } else { panic!("Unexpected event"); }
+               expect_payment_failed!(nodes[1], second_payment_hash, false);
        }
 }
 
@@ -6047,14 +6042,7 @@ fn do_htlc_claim_previous_remote_commitment_only(use_dust: bool, check_revoke_no
                check_added_monitors!(nodes[0], 1);
                check_closed_event!(nodes[0], 1, ClosureReason::CommitmentTxConfirmed);
        } else {
-               let events = nodes[0].node.get_and_clear_pending_events();
-               assert_eq!(events.len(), 2);
-               if let Event::PaymentPathFailed { ref payment_hash, .. } = events[0] {
-                       assert_eq!(*payment_hash, our_payment_hash);
-               } else { panic!("Unexpected event"); }
-               if let Event::PaymentFailed { ref payment_hash, .. } = events[1] {
-                       assert_eq!(*payment_hash, our_payment_hash);
-               } else { panic!("Unexpected event"); }
+               expect_payment_failed!(nodes[0], our_payment_hash, true);
        }
 }
 
index f4490d68aa9325ee8ab2c175da9c2f2fc92f7a6d..af66c0c360c81afdb6469b7d42ee37f7c0545df0 100644 (file)
@@ -404,8 +404,8 @@ pub enum Event {
        /// provide failure information for each MPP part in the payment.
        ///
        /// This event is provided once there are no further pending HTLCs for the payment and the
-       /// payment is no longer retryable, either due to a several-block timeout or because
-       /// [`ChannelManager::abandon_payment`] was previously called for the corresponding payment.
+       /// payment is no longer retryable due to [`ChannelManager::abandon_payment`] having been
+       /// called for the corresponding payment.
        ///
        /// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
        PaymentFailed {
@@ -444,9 +444,14 @@ pub enum Event {
        /// Indicates an outbound HTLC we sent failed. Probably some intermediary node dropped
        /// something. You may wish to retry with a different route.
        ///
+       /// If you have given up retrying this payment and wish to fail it, you MUST call
+       /// [`ChannelManager::abandon_payment`] at least once for a given [`PaymentId`] or memory
+       /// related to payment tracking will leak.
+       ///
        /// Note that this does *not* indicate that all paths for an MPP payment have failed, see
        /// [`Event::PaymentFailed`] and [`all_paths_failed`].
        ///
+       /// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
        /// [`all_paths_failed`]: Self::PaymentPathFailed::all_paths_failed
        PaymentPathFailed {
                /// The id returned by [`ChannelManager::send_payment`] and used with