@@ -1032,6 +1032,11 @@ impl Bolt12Invoice {
10321032 InvoiceContents :: ForRefund { .. } => self . message_paths ( ) . is_empty ( ) ,
10331033 }
10341034 }
1035+
1036+ /// Returns the [`TaggedHash`] of the invoice that was signed.
1037+ pub fn tagged_hash ( & self ) -> & TaggedHash {
1038+ & self . tagged_hash
1039+ }
10351040}
10361041
10371042impl PartialEq for Bolt12Invoice {
@@ -3560,4 +3565,97 @@ mod tests {
35603565 ) ,
35613566 }
35623567 }
3568+
3569+ #[ test]
3570+ fn invoice_offer_id_matches_offer_id ( ) {
3571+ let expanded_key = ExpandedKey :: new ( [ 42 ; 32 ] ) ;
3572+ let entropy = FixedEntropy { } ;
3573+ let nonce = Nonce :: from_entropy_source ( & entropy) ;
3574+ let secp_ctx = Secp256k1 :: new ( ) ;
3575+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
3576+
3577+ let offer = OfferBuilder :: new ( recipient_pubkey ( ) ) . amount_msats ( 1000 ) . build ( ) . unwrap ( ) ;
3578+
3579+ let offer_id = offer. id ( ) ;
3580+
3581+ let invoice_request = offer
3582+ . request_invoice ( & expanded_key, nonce, & secp_ctx, payment_id)
3583+ . unwrap ( )
3584+ . build_and_sign ( )
3585+ . unwrap ( ) ;
3586+
3587+ let invoice = invoice_request
3588+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) )
3589+ . unwrap ( )
3590+ . build ( )
3591+ . unwrap ( )
3592+ . sign ( recipient_sign)
3593+ . unwrap ( ) ;
3594+
3595+ assert_eq ! ( invoice. offer_id( ) , Some ( offer_id) ) ;
3596+ }
3597+
3598+ #[ test]
3599+ fn refund_invoice_has_no_offer_id ( ) {
3600+ let refund =
3601+ RefundBuilder :: new ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( ) . build ( ) . unwrap ( ) ;
3602+
3603+ let invoice = refund
3604+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , recipient_pubkey ( ) , now ( ) )
3605+ . unwrap ( )
3606+ . build ( )
3607+ . unwrap ( )
3608+ . sign ( recipient_sign)
3609+ . unwrap ( ) ;
3610+
3611+ assert_eq ! ( invoice. offer_id( ) , None ) ;
3612+ }
3613+
3614+ #[ test]
3615+ fn verifies_invoice_signature_with_tagged_hash ( ) {
3616+ use crate :: ln:: channelmanager:: PaymentId ;
3617+ use crate :: ln:: inbound_payment:: ExpandedKey ;
3618+ use crate :: offers:: merkle;
3619+ use crate :: offers:: nonce:: Nonce ;
3620+ use crate :: offers:: offer:: OfferBuilder ;
3621+ use crate :: offers:: test_utils:: {
3622+ payment_hash, payment_paths, recipient_pubkey, recipient_sign, FixedEntropy ,
3623+ } ;
3624+ use bitcoin:: secp256k1:: Secp256k1 ;
3625+ use core:: time:: Duration ;
3626+
3627+ let secp_ctx = Secp256k1 :: new ( ) ;
3628+ let expanded_key = ExpandedKey :: new ( [ 42 ; 32 ] ) ;
3629+ let entropy = FixedEntropy { } ;
3630+ let nonce = Nonce :: from_entropy_source ( & entropy) ;
3631+ let node_id = recipient_pubkey ( ) ;
3632+ let payment_paths = payment_paths ( ) ;
3633+ let now = Duration :: from_secs ( 123456 ) ;
3634+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
3635+
3636+ let offer = OfferBuilder :: new ( node_id)
3637+ . amount_msats ( 1000 )
3638+ . path ( crate :: offers:: test_utils:: blinded_path ( ) )
3639+ . build ( )
3640+ . unwrap ( ) ;
3641+
3642+ let invoice_request = offer
3643+ . request_invoice ( & expanded_key, nonce, & secp_ctx, payment_id)
3644+ . unwrap ( )
3645+ . build_and_sign ( )
3646+ . unwrap ( ) ;
3647+
3648+ let invoice = invoice_request
3649+ . respond_with_no_std ( payment_paths, payment_hash ( ) , now)
3650+ . unwrap ( )
3651+ . build ( )
3652+ . unwrap ( )
3653+ . sign ( recipient_sign)
3654+ . unwrap ( ) ;
3655+
3656+ let issuer_sign_pubkey = offer. issuer_signing_pubkey ( ) . unwrap ( ) ;
3657+ let tagged_hash = invoice. tagged_hash ( ) ;
3658+ let signature = invoice. signature ( ) ;
3659+ assert ! ( merkle:: verify_signature( & signature, tagged_hash, issuer_sign_pubkey) . is_ok( ) ) ;
3660+ }
35633661}
0 commit comments