session_privs: HashSet<[u8; 32]>,
},
AwaitingInvoice {
- absolute_expiry: Duration,
+ expiration: StaleExpiration,
retry_strategy: Retry,
max_total_routing_fee_msat: Option<u64>,
},
}
}
+/// How long before a [`PendingOutboundPayment::AwaitingInvoice`] should be considered stale and
+/// candidate for removal in [`OutboundPayments::remove_stale_payments`].
+#[derive(Clone, Copy)]
+pub(crate) enum StaleExpiration {
+ /// Number of times [`OutboundPayments::remove_stale_payments`] is called.
+ TimerTicks(u64),
+ /// Duration since the Unix epoch.
+ AbsoluteTimeout(core::time::Duration),
+}
+
+impl_writeable_tlv_based_enum!(StaleExpiration,
+ ;
+ (0, TimerTicks),
+ (2, AbsoluteTimeout)
+);
+
/// Indicates an immediate error on [`ChannelManager::send_payment`]. Further errors may be
/// surfaced later via [`Event::PaymentPathFailed`] and [`Event::PaymentFailed`].
///
}
pub(super) fn add_new_awaiting_invoice(
- &self, payment_id: PaymentId, absolute_expiry: Duration, retry_strategy: Retry,
+ &self, payment_id: PaymentId, expiration: StaleExpiration, retry_strategy: Retry,
max_total_routing_fee_msat: Option<u64>
) -> Result<(), ()> {
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
hash_map::Entry::Occupied(_) => Err(()),
hash_map::Entry::Vacant(entry) => {
entry.insert(PendingOutboundPayment::AwaitingInvoice {
- absolute_expiry,
+ expiration,
retry_strategy,
max_total_routing_fee_msat,
});
true
}
},
- PendingOutboundPayment::AwaitingInvoice { absolute_expiry, .. } => {
- if duration_since_epoch < *absolute_expiry {
- true
- } else {
+ PendingOutboundPayment::AwaitingInvoice { expiration, .. } => {
+ let is_stale = match expiration {
+ StaleExpiration::AbsoluteTimeout(absolute_expiry) => {
+ *absolute_expiry <= duration_since_epoch
+ },
+ StaleExpiration::TimerTicks(timer_ticks_remaining) => {
+ if *timer_ticks_remaining > 0 {
+ *timer_ticks_remaining -= 1;
+ false
+ } else {
+ true
+ }
+ },
+ };
+ if is_stale {
#[cfg(invreqfailed)]
pending_events.push_back(
(events::Event::InvoiceRequestFailed { payment_id: *payment_id }, None)
);
false
+ } else {
+ true
}
},
_ => true,
(2, payment_hash, required),
},
(5, AwaitingInvoice) => {
- (0, absolute_expiry, required),
+ (0, expiration, required),
(2, retry_strategy, required),
(4, max_total_routing_fee_msat, option),
},
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};
+ use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, Retry, RetryableSendFailure, StaleExpiration};
use crate::offers::invoice::DEFAULT_RELATIVE_EXPIRY;
use crate::offers::offer::OfferBuilder;
use crate::offers::test_utils::*;
#[test]
#[cfg(invreqfailed)]
- fn removes_stale_awaiting_invoice() {
+ fn removes_stale_awaiting_invoice_using_absolute_timeout() {
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
let absolute_expiry = 100;
let tick_interval = 10;
+ let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(absolute_expiry));
assert!(!outbound_payments.has_pending_payments());
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), None
+ payment_id, expiration, Retry::Attempts(0), None
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, Duration::from_secs(absolute_expiry + 1), Retry::Attempts(0), None
+ payment_id, expiration, Retry::Attempts(0), None
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, Duration::from_secs(absolute_expiry + 1), Retry::Attempts(0), None
+ payment_id, expiration, Retry::Attempts(0), None
+ ).is_err()
+ );
+ }
+
+ #[test]
+ #[cfg(invreqfailed)]
+ fn removes_stale_awaiting_invoice_using_timer_ticks() {
+ let pending_events = Mutex::new(VecDeque::new());
+ let outbound_payments = OutboundPayments::new();
+ let payment_id = PaymentId([0; 32]);
+ let timer_ticks = 3;
+ let expiration = StaleExpiration::TimerTicks(timer_ticks);
+
+ assert!(!outbound_payments.has_pending_payments());
+ assert!(
+ outbound_payments.add_new_awaiting_invoice(
+ payment_id, expiration, Retry::Attempts(0), None
+ ).is_ok()
+ );
+ assert!(outbound_payments.has_pending_payments());
+
+ for i in 0..timer_ticks {
+ let duration_since_epoch = Duration::from_secs(i * 60);
+ outbound_payments.remove_stale_payments(duration_since_epoch, &pending_events);
+
+ assert!(outbound_payments.has_pending_payments());
+ assert!(pending_events.lock().unwrap().is_empty());
+ }
+
+ let duration_since_epoch = Duration::from_secs(timer_ticks * 60);
+ outbound_payments.remove_stale_payments(duration_since_epoch, &pending_events);
+
+ assert!(!outbound_payments.has_pending_payments());
+ assert!(!pending_events.lock().unwrap().is_empty());
+ assert_eq!(
+ pending_events.lock().unwrap().pop_front(),
+ Some((Event::InvoiceRequestFailed { payment_id }, None)),
+ );
+ assert!(pending_events.lock().unwrap().is_empty());
+
+ assert!(
+ outbound_payments.add_new_awaiting_invoice(
+ payment_id, expiration, Retry::Attempts(0), None
+ ).is_ok()
+ );
+ assert!(outbound_payments.has_pending_payments());
+
+ assert!(
+ outbound_payments.add_new_awaiting_invoice(
+ payment_id, expiration, Retry::Attempts(0), None
).is_err()
);
}
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
- let absolute_expiry = 100;
+ let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
assert!(!outbound_payments.has_pending_payments());
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), None
+ payment_id, expiration, Retry::Attempts(0), None
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
- let absolute_expiry = 100;
+ let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), None
+ payment_id, expiration, Retry::Attempts(0), None
).is_ok()
);
assert!(outbound_payments.has_pending_payments());
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
- let absolute_expiry = 100;
+ let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0),
+ payment_id, expiration, Retry::Attempts(0),
Some(invoice.amount_msats() / 100 + 50_000)
).is_ok()
);
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
- let absolute_expiry = 100;
+ let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0),
+ payment_id, expiration, Retry::Attempts(0),
Some(invoice.amount_msats() / 100 + 50_000)
).is_ok()
);
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
- let absolute_expiry = 100;
+ let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
assert!(
outbound_payments.add_new_awaiting_invoice(
- payment_id, Duration::from_secs(absolute_expiry), Retry::Attempts(0), Some(1234)
+ payment_id, expiration, Retry::Attempts(0), Some(1234)
).is_ok()
);
assert!(outbound_payments.has_pending_payments());