@@ -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 {
@@ -1785,7 +1790,6 @@ mod tests {
17851790 use bitcoin:: script:: ScriptBuf ;
17861791 use bitcoin:: secp256k1:: { self , Keypair , Message , Secp256k1 , SecretKey , XOnlyPublicKey } ;
17871792 use bitcoin:: { CompressedPublicKey , WitnessProgram , WitnessVersion } ;
1788-
17891793 use core:: time:: Duration ;
17901794
17911795 use crate :: blinded_path:: message:: BlindedMessagePath ;
@@ -3560,4 +3564,86 @@ mod tests {
35603564 ) ,
35613565 }
35623566 }
3567+
3568+ #[ test]
3569+ fn invoice_offer_id_matches_offer_id ( ) {
3570+ let expanded_key = ExpandedKey :: new ( [ 42 ; 32 ] ) ;
3571+ let entropy = FixedEntropy { } ;
3572+ let nonce = Nonce :: from_entropy_source ( & entropy) ;
3573+ let secp_ctx = Secp256k1 :: new ( ) ;
3574+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
3575+
3576+ let offer = OfferBuilder :: new ( recipient_pubkey ( ) ) . amount_msats ( 1000 ) . build ( ) . unwrap ( ) ;
3577+
3578+ let offer_id = offer. id ( ) ;
3579+
3580+ let invoice_request = offer
3581+ . request_invoice ( & expanded_key, nonce, & secp_ctx, payment_id)
3582+ . unwrap ( )
3583+ . build_and_sign ( )
3584+ . unwrap ( ) ;
3585+
3586+ let invoice = invoice_request
3587+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) )
3588+ . unwrap ( )
3589+ . build ( )
3590+ . unwrap ( )
3591+ . sign ( recipient_sign)
3592+ . unwrap ( ) ;
3593+
3594+ assert_eq ! ( invoice. offer_id( ) , Some ( offer_id) ) ;
3595+ }
3596+
3597+ #[ test]
3598+ fn refund_invoice_has_no_offer_id ( ) {
3599+ let refund =
3600+ RefundBuilder :: new ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( ) . build ( ) . unwrap ( ) ;
3601+
3602+ let invoice = refund
3603+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , recipient_pubkey ( ) , now ( ) )
3604+ . unwrap ( )
3605+ . build ( )
3606+ . unwrap ( )
3607+ . sign ( recipient_sign)
3608+ . unwrap ( ) ;
3609+
3610+ assert_eq ! ( invoice. offer_id( ) , None ) ;
3611+ }
3612+
3613+ #[ test]
3614+ fn verifies_invoice_signature_with_tagged_hash ( ) {
3615+ let secp_ctx = Secp256k1 :: new ( ) ;
3616+ let expanded_key = ExpandedKey :: new ( [ 42 ; 32 ] ) ;
3617+ let entropy = FixedEntropy { } ;
3618+ let nonce = Nonce :: from_entropy_source ( & entropy) ;
3619+ let node_id = recipient_pubkey ( ) ;
3620+ let payment_paths = payment_paths ( ) ;
3621+ let now = Duration :: from_secs ( 123456 ) ;
3622+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
3623+
3624+ let offer = OfferBuilder :: new ( node_id)
3625+ . amount_msats ( 1000 )
3626+ . path ( crate :: offers:: test_utils:: blinded_path ( ) )
3627+ . build ( )
3628+ . unwrap ( ) ;
3629+
3630+ let invoice_request = offer
3631+ . request_invoice ( & expanded_key, nonce, & secp_ctx, payment_id)
3632+ . unwrap ( )
3633+ . build_and_sign ( )
3634+ . unwrap ( ) ;
3635+
3636+ let invoice = invoice_request
3637+ . respond_with_no_std ( payment_paths, payment_hash ( ) , now)
3638+ . unwrap ( )
3639+ . build ( )
3640+ . unwrap ( )
3641+ . sign ( recipient_sign)
3642+ . unwrap ( ) ;
3643+
3644+ let issuer_sign_pubkey = offer. issuer_signing_pubkey ( ) . unwrap ( ) ;
3645+ let tagged_hash = invoice. tagged_hash ( ) ;
3646+ let signature = invoice. signature ( ) ;
3647+ assert ! ( merkle:: verify_signature( & signature, tagged_hash, issuer_sign_pubkey) . is_ok( ) ) ;
3648+ }
35633649}
0 commit comments