@@ -114,6 +114,7 @@ use secp256k1::key::PublicKey;
114114use std:: collections:: hash_map:: { self , HashMap } ;
115115use std:: ops:: Deref ;
116116use std:: sync:: Mutex ;
117+ use std:: time:: { Duration , SystemTime } ;
117118
118119/// A utility for paying [`Invoice]`s.
119120pub struct InvoicePayer < P : Deref , R , L : Deref , E >
@@ -225,6 +226,7 @@ where
225226 hash_map:: Entry :: Vacant ( entry) => {
226227 let payer = self . payer . node_id ( ) ;
227228 let mut payee = Payee :: new ( invoice. recover_payee_pub_key ( ) )
229+ . with_expiry_time ( expiry_time_from_unix_epoch ( & invoice) )
228230 . with_route_hints ( invoice. route_hints ( ) ) ;
229231 if let Some ( features) = invoice. features ( ) {
230232 payee = payee. with_features ( features. clone ( ) ) ;
@@ -272,6 +274,14 @@ where
272274 }
273275}
274276
277+ fn expiry_time_from_unix_epoch ( invoice : & Invoice ) -> Duration {
278+ invoice. timestamp ( ) . duration_since ( SystemTime :: UNIX_EPOCH ) . unwrap ( ) + invoice. expiry_time ( )
279+ }
280+
281+ fn has_expired ( params : & RouteParameters ) -> bool {
282+ Invoice :: is_expired_from_epoch ( & SystemTime :: UNIX_EPOCH , params. payee . expiry_time . unwrap ( ) )
283+ }
284+
275285impl < P : Deref , R , L : Deref , E > EventHandler for InvoicePayer < P , R , L , E >
276286where
277287 P :: Target : Payer ,
@@ -296,6 +306,8 @@ where
296306 log_trace ! ( self . logger, "Payment {} exceeded maximum attempts; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
297307 } else if retry. is_none ( ) {
298308 log_trace ! ( self . logger, "Payment {} missing retry params; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
309+ } else if has_expired ( retry. as_ref ( ) . unwrap ( ) ) {
310+ log_trace ! ( self . logger, "Invoice expired for payment {}; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
299311 } else if self . retry_payment ( * payment_id. as_ref ( ) . unwrap ( ) , retry. as_ref ( ) . unwrap ( ) ) . is_err ( ) {
300312 log_trace ! ( self . logger, "Error retrying payment {}; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
301313 } else {
@@ -328,7 +340,7 @@ where
328340#[ cfg( test) ]
329341mod tests {
330342 use super :: * ;
331- use crate :: { InvoiceBuilder , Currency } ;
343+ use crate :: { DEFAULT_EXPIRY_TIME , InvoiceBuilder , Currency } ;
332344 use bitcoin_hashes:: sha256:: Hash as Sha256 ;
333345 use lightning:: ln:: PaymentPreimage ;
334346 use lightning:: ln:: features:: { ChannelFeatures , NodeFeatures } ;
@@ -338,6 +350,7 @@ mod tests {
338350 use lightning:: util:: errors:: APIError ;
339351 use lightning:: util:: events:: Event ;
340352 use secp256k1:: { SecretKey , PublicKey , Secp256k1 } ;
353+ use std:: time:: { SystemTime , Duration } ;
341354
342355 fn invoice ( payment_preimage : PaymentPreimage ) -> Invoice {
343356 let payment_hash = Sha256 :: hash ( & payment_preimage. 0 ) ;
@@ -370,6 +383,25 @@ mod tests {
370383 . unwrap ( )
371384 }
372385
386+ fn expired_invoice ( payment_preimage : PaymentPreimage ) -> Invoice {
387+ let payment_hash = Sha256 :: hash ( & payment_preimage. 0 ) ;
388+ let private_key = SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ;
389+ let timestamp = SystemTime :: now ( )
390+ . checked_sub ( Duration :: from_secs ( DEFAULT_EXPIRY_TIME * 2 ) )
391+ . unwrap ( ) ;
392+ InvoiceBuilder :: new ( Currency :: Bitcoin )
393+ . description ( "test" . into ( ) )
394+ . payment_hash ( payment_hash)
395+ . payment_secret ( PaymentSecret ( [ 0 ; 32 ] ) )
396+ . timestamp ( timestamp)
397+ . min_final_cltv_expiry ( 144 )
398+ . amount_milli_satoshis ( 128 )
399+ . build_signed ( |hash| {
400+ Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key)
401+ } )
402+ . unwrap ( )
403+ }
404+
373405 #[ test]
374406 fn pays_invoice_on_first_attempt ( ) {
375407 let event_handled = core:: cell:: RefCell :: new ( false ) ;
@@ -524,6 +556,37 @@ mod tests {
524556 assert_eq ! ( * payer. attempts. borrow( ) , 1 ) ;
525557 }
526558
559+ #[ test]
560+ fn fails_paying_invoice_after_expiration ( ) {
561+ let event_handled = core:: cell:: RefCell :: new ( false ) ;
562+ let event_handler = |_: & _ | { * event_handled. borrow_mut ( ) = true ; } ;
563+
564+ let payer = TestPayer :: new ( ) ;
565+ let router = TestRouter { } ;
566+ let logger = TestLogger :: new ( ) ;
567+ let invoice_payer =
568+ InvoicePayer :: new ( & payer, router, & logger, event_handler, RetryAttempts ( 2 ) ) ;
569+
570+ let payment_preimage = PaymentPreimage ( [ 1 ; 32 ] ) ;
571+ let invoice = expired_invoice ( payment_preimage) ;
572+ let payment_id = Some ( invoice_payer. pay_invoice ( & invoice) . unwrap ( ) ) ;
573+ assert_eq ! ( * payer. attempts. borrow( ) , 1 ) ;
574+
575+ let event = Event :: PaymentPathFailed {
576+ payment_id,
577+ payment_hash : PaymentHash ( invoice. payment_hash ( ) . clone ( ) . into_inner ( ) ) ,
578+ network_update : None ,
579+ rejected_by_dest : false ,
580+ all_paths_failed : false ,
581+ path : vec ! [ ] ,
582+ short_channel_id : None ,
583+ retry : Some ( TestRouter :: retry_for_invoice ( & invoice) ) ,
584+ } ;
585+ invoice_payer. handle_event ( & event) ;
586+ assert_eq ! ( * event_handled. borrow( ) , true ) ;
587+ assert_eq ! ( * payer. attempts. borrow( ) , 1 ) ;
588+ }
589+
527590 #[ test]
528591 fn fails_paying_invoice_after_retry_error ( ) {
529592 let event_handled = core:: cell:: RefCell :: new ( false ) ;
@@ -745,6 +808,7 @@ mod tests {
745808
746809 fn retry_for_invoice ( invoice : & Invoice ) -> RouteParameters {
747810 let mut payee = Payee :: new ( invoice. recover_payee_pub_key ( ) )
811+ . with_expiry_time ( expiry_time_from_unix_epoch ( invoice) )
748812 . with_route_hints ( invoice. route_hints ( ) ) ;
749813 if let Some ( features) = invoice. features ( ) {
750814 payee = payee. with_features ( features. clone ( ) ) ;
0 commit comments