From 41728b8364e3814e6e48ac368c4a2fc32c6e8195 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Thu, 20 Jun 2024 15:21:20 -0400 Subject: [PATCH] Timeout expired outbound async payments. --- lightning/src/ln/outbound_payment.rs | 67 +++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index 83e9fa435a1..06b3260625e 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -1757,6 +1757,22 @@ impl OutboundPayments { true } }, + PendingOutboundPayment::StaticInvoiceReceived { route_params, payment_hash, .. } => { + let is_stale = + route_params.payment_params.expiry_time.unwrap_or_else(u64::max_value) < + duration_since_epoch.as_secs(); + if is_stale { + let fail_ev = events::Event::PaymentFailed { + payment_id: *payment_id, + payment_hash: *payment_hash, + reason: Some(PaymentFailureReason::PaymentExpired) + }; + pending_events.push_back((fail_ev, None)); + false + } else { + true + } + }, _ => true, }); } @@ -2008,11 +2024,11 @@ mod tests { use crate::blinded_path::EmptyNodeIdLookUp; use crate::events::{Event, PathFailure, PaymentFailureReason}; - use crate::ln::types::PaymentHash; + use crate::ln::types::{PaymentHash, PaymentPreimage}; use crate::ln::channelmanager::{PaymentId, RecipientOnionFields}; use crate::ln::features::{ChannelFeatures, NodeFeatures}; use crate::ln::msgs::{ErrorAction, LightningError}; - use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, Retry, RetryableSendFailure, StaleExpiration}; + use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, PendingOutboundPayment, Retry, RetryableSendFailure, StaleExpiration}; #[cfg(feature = "std")] use crate::offers::invoice::DEFAULT_RELATIVE_EXPIRY; use crate::offers::offer::OfferBuilder; @@ -2551,4 +2567,51 @@ mod tests { assert!(outbound_payments.has_pending_payments()); assert!(pending_events.lock().unwrap().is_empty()); } + + #[test] + fn time_out_unreleased_async_payments() { + let pending_events = Mutex::new(VecDeque::new()); + let outbound_payments = OutboundPayments::new(); + 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); + + // The payment will not be removed if it isn't expired yet. + outbound_payments.remove_stale_payments(Duration::from_secs(absolute_expiry), &pending_events); + let outbounds = outbound_payments.pending_outbound_payments.lock().unwrap(); + assert_eq!(outbounds.len(), 1); + let events = pending_events.lock().unwrap(); + assert_eq!(events.len(), 0); + core::mem::drop(outbounds); + core::mem::drop(events); + + outbound_payments.remove_stale_payments(Duration::from_secs(absolute_expiry + 1), &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, + payment_id, + reason: Some(PaymentFailureReason::PaymentExpired), + }, None)); + } }