@@ -579,3 +579,74 @@ fn pays_static_invoice() {
579
579
. handle_onion_message ( nodes[ 2 ] . node . get_our_node_id ( ) , & release_held_htlc_om) ;
580
580
assert ! ( nodes[ 0 ] . node. get_and_clear_pending_msg_events( ) . is_empty( ) ) ;
581
581
}
582
+
583
+ #[ cfg_attr( feature = "std" , ignore) ]
584
+ #[ test]
585
+ fn expired_static_invoice_fail ( ) {
586
+ // Test that if we receive an expired static invoice we'll fail the payment.
587
+ let secp_ctx = Secp256k1 :: new ( ) ;
588
+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
589
+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
590
+ let node_chanmgrs = create_node_chanmgrs ( 3 , & node_cfgs, & [ None , None , None ] ) ;
591
+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
592
+ create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1_000_000 , 0 ) ;
593
+ create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 2 , 1_000_000 , 0 ) ;
594
+
595
+ const INVOICE_EXPIRY_SECS : u32 = 10 ;
596
+ let relative_expiry = Duration :: from_secs ( INVOICE_EXPIRY_SECS as u64 ) ;
597
+ let ( offer, static_invoice) =
598
+ create_static_invoice ( & nodes[ 1 ] , & nodes[ 2 ] , Some ( relative_expiry) , & secp_ctx) ;
599
+
600
+ let amt_msat = 5000 ;
601
+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
602
+ nodes[ 0 ]
603
+ . node
604
+ . pay_for_offer ( & offer, None , Some ( amt_msat) , None , payment_id, Retry :: Attempts ( 0 ) , None )
605
+ . unwrap ( ) ;
606
+
607
+ let invreq_om = nodes[ 0 ]
608
+ . onion_messenger
609
+ . next_onion_message_for_peer ( nodes[ 1 ] . node . get_our_node_id ( ) )
610
+ . unwrap ( ) ;
611
+ let invreq_reply_path = offers_tests:: extract_invoice_request ( & nodes[ 1 ] , & invreq_om) . 1 ;
612
+ // TODO: update to not manually send here when we add support for being the recipient's
613
+ // always-online counterparty
614
+ nodes[ 1 ]
615
+ . onion_messenger
616
+ . send_onion_message (
617
+ ParsedOnionMessageContents :: < Infallible > :: Offers ( OffersMessage :: StaticInvoice (
618
+ static_invoice,
619
+ ) ) ,
620
+ MessageSendInstructions :: WithoutReplyPath {
621
+ destination : Destination :: BlindedPath ( invreq_reply_path) ,
622
+ } ,
623
+ )
624
+ . unwrap ( ) ;
625
+ let static_invoice_om = nodes[ 1 ]
626
+ . onion_messenger
627
+ . next_onion_message_for_peer ( nodes[ 0 ] . node . get_our_node_id ( ) )
628
+ . unwrap ( ) ;
629
+
630
+ // Wait until the static invoice expires before providing it to the sender.
631
+ let block = create_dummy_block (
632
+ nodes[ 0 ] . best_block_hash ( ) ,
633
+ nodes[ 0 ] . node . duration_since_epoch ( ) . as_secs ( ) as u32 + INVOICE_EXPIRY_SECS + 1 ,
634
+ Vec :: new ( ) ,
635
+ ) ;
636
+ connect_block ( & nodes[ 0 ] , & block) ;
637
+ nodes[ 0 ]
638
+ . onion_messenger
639
+ . handle_onion_message ( nodes[ 1 ] . node . get_our_node_id ( ) , & static_invoice_om) ;
640
+
641
+ let events = nodes[ 0 ] . node . get_and_clear_pending_events ( ) ;
642
+ assert_eq ! ( events. len( ) , 1 ) ;
643
+ match events[ 0 ] {
644
+ Event :: PaymentFailed { payment_id : ev_payment_id, reason, .. } => {
645
+ assert_eq ! ( reason. unwrap( ) , PaymentFailureReason :: PaymentExpired ) ;
646
+ assert_eq ! ( ev_payment_id, payment_id) ;
647
+ } ,
648
+ _ => panic ! ( ) ,
649
+ }
650
+ // The sender doesn't reply with InvoiceError right now because the always-online node doesn't
651
+ // currently provide them with a reply path to do so.
652
+ }
0 commit comments