Skip to content

Commit 0b0d17e

Browse files
committed
Check for invoice expiry in InvoicePayer before we send any HTLCs
1 parent 0c82577 commit 0b0d17e

File tree

1 file changed

+42
-0
lines changed

1 file changed

+42
-0
lines changed

lightning-invoice/src/payment.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,10 @@ where
269269
final_value_msat: invoice.amount_milli_satoshis().or(amount_msats).unwrap(),
270270
final_cltv_expiry_delta: invoice.min_final_cltv_expiry() as u32,
271271
};
272+
if has_expired(&params) {
273+
log_trace!(self.logger, "Invoice expired prior to first send for payment {}", log_bytes!(payment_hash.0));
274+
return Err(PaymentError::Invoice("Invoice expired prior to send"));
275+
}
272276
let first_hops = self.payer.first_hops();
273277
let route = self.router.find_route(
274278
&payer,
@@ -513,6 +517,25 @@ mod tests {
513517
.unwrap()
514518
}
515519

520+
fn will_expire_in_1_sec_invoice(payment_preimage: PaymentPreimage) -> Invoice {
521+
let payment_hash = Sha256::hash(&payment_preimage.0);
522+
let private_key = SecretKey::from_slice(&[42; 32]).unwrap();
523+
let timestamp = SystemTime::now()
524+
.checked_sub(Duration::from_secs(DEFAULT_EXPIRY_TIME - 1))
525+
.unwrap();
526+
InvoiceBuilder::new(Currency::Bitcoin)
527+
.description("test".into())
528+
.payment_hash(payment_hash)
529+
.payment_secret(PaymentSecret([0; 32]))
530+
.timestamp(timestamp)
531+
.min_final_cltv_expiry(144)
532+
.amount_milli_satoshis(128)
533+
.build_signed(|hash| {
534+
Secp256k1::new().sign_recoverable(hash, &private_key)
535+
})
536+
.unwrap()
537+
}
538+
516539
#[test]
517540
fn pays_invoice_on_first_attempt() {
518541
let event_handled = core::cell::RefCell::new(false);
@@ -728,7 +751,26 @@ mod tests {
728751

729752
let payment_preimage = PaymentPreimage([1; 32]);
730753
let invoice = expired_invoice(payment_preimage);
754+
if let PaymentError::Invoice(msg) = invoice_payer.pay_invoice(&invoice).unwrap_err() {
755+
assert_eq!(msg, "Invoice expired prior to send");
756+
} else { panic!("Expected Invoice Error"); }
757+
}
758+
759+
#[test]
760+
fn fails_retrying_invoice_after_expiration() {
761+
let event_handled = core::cell::RefCell::new(false);
762+
let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
763+
764+
let payer = TestPayer::new();
765+
let router = TestRouter {};
766+
let logger = TestLogger::new();
767+
let invoice_payer =
768+
InvoicePayer::new(&payer, router, &logger, event_handler, RetryAttempts(2));
769+
770+
let payment_preimage = PaymentPreimage([1; 32]);
771+
let invoice = will_expire_in_1_sec_invoice(payment_preimage);
731772
let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
773+
std::thread::sleep(Duration::from_secs(2));
732774
assert_eq!(*payer.attempts.borrow(), 1);
733775

734776
let event = Event::PaymentPathFailed {

0 commit comments

Comments
 (0)