@@ -1032,6 +1032,11 @@ impl Bolt12Invoice {
1032
1032
InvoiceContents :: ForRefund { .. } => self . message_paths ( ) . is_empty ( ) ,
1033
1033
}
1034
1034
}
1035
+
1036
+ /// Returns the [`TaggedHash`] of the invoice that was signed.
1037
+ pub fn tagged_hash ( & self ) -> & TaggedHash {
1038
+ & self . tagged_hash
1039
+ }
1035
1040
}
1036
1041
1037
1042
impl PartialEq for Bolt12Invoice {
@@ -1785,7 +1790,6 @@ mod tests {
1785
1790
use bitcoin:: script:: ScriptBuf ;
1786
1791
use bitcoin:: secp256k1:: { self , Keypair , Message , Secp256k1 , SecretKey , XOnlyPublicKey } ;
1787
1792
use bitcoin:: { CompressedPublicKey , WitnessProgram , WitnessVersion } ;
1788
-
1789
1793
use core:: time:: Duration ;
1790
1794
1791
1795
use crate :: blinded_path:: message:: BlindedMessagePath ;
@@ -3560,4 +3564,86 @@ mod tests {
3560
3564
) ,
3561
3565
}
3562
3566
}
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
+ }
3563
3649
}
0 commit comments