From: Matt Corallo Date: Sat, 8 Oct 2022 19:54:43 +0000 (+0000) Subject: Stop timing out payments automatically, requiring abandon_payment X-Git-Tag: v0.0.113~58^2~3 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=548f3f841660b5da81e6cf46573d400a9fcbf3ea;p=rust-lightning Stop timing out payments automatically, requiring abandon_payment 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. --- diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 5a98289c5..2e7e0d3c3 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -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 { diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 30120225b..0709b64fc 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -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); } } diff --git a/lightning/src/util/events.rs b/lightning/src/util/events.rs index f4490d68a..af66c0c36 100644 --- a/lightning/src/util/events.rs +++ b/lightning/src/util/events.rs @@ -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