@@ -408,7 +408,7 @@ impl StaticInvoice {
408408 /// Whether the [`Offer`] that this invoice is based on is expired.
409409 #[ cfg( feature = "std" ) ]
410410 pub fn is_offer_expired ( & self ) -> bool {
411- self . contents . is_expired ( )
411+ self . contents . is_offer_expired ( )
412412 }
413413
414414 /// Whether the [`Offer`] that this invoice is based on is expired, given the current time as
@@ -1003,6 +1003,43 @@ mod tests {
10031003 }
10041004 }
10051005
1006+ #[ cfg( feature = "std" ) ]
1007+ #[ test]
1008+ fn is_offer_expired_does_not_check_invoice_expiry ( ) {
1009+ // Regression test: `StaticInvoice::is_offer_expired` must reflect the offer's expiry,
1010+ // not the invoice's own expiry. Build an invoice whose offer has no absolute expiry
1011+ // (so the offer never expires) but whose own `created_at + relative_expiry` lies in
1012+ // the past (so the invoice itself is expired).
1013+ let node_id = recipient_pubkey ( ) ;
1014+ let payment_paths = payment_paths ( ) ;
1015+ let expanded_key = ExpandedKey :: new ( [ 42 ; 32 ] ) ;
1016+ let entropy = FixedEntropy { } ;
1017+ let nonce = Nonce :: from_entropy_source ( & entropy) ;
1018+ let secp_ctx = Secp256k1 :: new ( ) ;
1019+
1020+ let offer = OfferBuilder :: deriving_signing_pubkey ( node_id, & expanded_key, nonce, & secp_ctx)
1021+ . path ( blinded_path ( ) )
1022+ . build ( )
1023+ . unwrap ( ) ;
1024+
1025+ let invoice = StaticInvoiceBuilder :: for_offer_using_derived_keys (
1026+ & offer,
1027+ payment_paths. clone ( ) ,
1028+ vec ! [ blinded_path( ) ] ,
1029+ Duration :: from_secs ( 0 ) ,
1030+ & expanded_key,
1031+ nonce,
1032+ & secp_ctx,
1033+ )
1034+ . unwrap ( )
1035+ . relative_expiry ( 1 )
1036+ . build_and_sign ( & secp_ctx)
1037+ . unwrap ( ) ;
1038+
1039+ assert ! ( invoice. is_expired( ) ) ;
1040+ assert ! ( !invoice. is_offer_expired( ) ) ;
1041+ }
1042+
10061043 #[ test]
10071044 fn builds_invoice_from_offer_using_derived_key ( ) {
10081045 let node_id = recipient_pubkey ( ) ;
0 commit comments