From 6d415b15c24b4805c73de208fd389fb9a9dc1679 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Thu, 20 Jun 2024 16:28:36 -0400 Subject: [PATCH] Support abandoning pending outbound async payments. Async payments may have very high expires because we may be waiting for days for the recipient to come online, so it's important that users be able to abandon these payments early if needed. --- lightning/src/ln/outbound_payment.rs | 74 ++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index 476201a8e..65563325c 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -218,20 +218,26 @@ impl PendingOutboundPayment { } fn mark_abandoned(&mut self, reason: PaymentFailureReason) { - if let PendingOutboundPayment::Retryable { session_privs, payment_hash, .. } = self { - let mut our_session_privs = new_hash_set(); - core::mem::swap(&mut our_session_privs, session_privs); - *self = PendingOutboundPayment::Abandoned { - session_privs: our_session_privs, - payment_hash: *payment_hash, - reason: Some(reason) - }; - } else if let PendingOutboundPayment::InvoiceReceived { payment_hash, .. } = self { - *self = PendingOutboundPayment::Abandoned { - session_privs: new_hash_set(), - payment_hash: *payment_hash, - reason: Some(reason) - }; + let session_privs = match self { + PendingOutboundPayment::Retryable { session_privs, .. } => { + let mut our_session_privs = new_hash_set(); + core::mem::swap(&mut our_session_privs, session_privs); + our_session_privs + }, + _ => new_hash_set(), + }; + match self { + Self::Retryable { payment_hash, .. } | + Self::InvoiceReceived { payment_hash, .. } | + Self::StaticInvoiceReceived { payment_hash, .. } => + { + *self = Self::Abandoned { + session_privs, + payment_hash: *payment_hash, + reason: Some(reason), + }; + }, + _ => {} } } @@ -2789,4 +2795,44 @@ mod tests { reason: Some(PaymentFailureReason::PaymentExpired), }, None)); } + + #[test] + fn abandon_unreleased_async_payment() { + let pending_events = Mutex::new(VecDeque::new()); + let outbound_payments = OutboundPayments::new(new_hash_map()); + let payment_id = PaymentId([0; 32]); + let absolute_expiry = 60; + + let mut outbounds = outbound_payments.pending_outbound_payments.lock().unwrap(); + let payment_params = PaymentParameters::from_node_id(test_utils::pubkey(42), 0) + .with_expiry_time(absolute_expiry); + let route_params = RouteParameters { + payment_params, + final_value_msat: 0, + max_total_routing_fee_msat: None, + }; + let payment_hash = PaymentHash([0; 32]); + let outbound = PendingOutboundPayment::StaticInvoiceReceived { + payment_hash, + keysend_preimage: PaymentPreimage([0; 32]), + retry_strategy: Retry::Attempts(0), + payment_release_secret: [0; 32], + route_params, + }; + outbounds.insert(payment_id, outbound); + core::mem::drop(outbounds); + + outbound_payments.abandon_payment( + payment_id, PaymentFailureReason::UserAbandoned, &pending_events + ); + let outbounds = outbound_payments.pending_outbound_payments.lock().unwrap(); + assert_eq!(outbounds.len(), 0); + let events = pending_events.lock().unwrap(); + assert_eq!(events.len(), 1); + assert_eq!(events[0], (Event::PaymentFailed { + payment_hash: Some(payment_hash), + payment_id, + reason: Some(PaymentFailureReason::UserAbandoned), + }, None)); + } } -- 2.39.5