@@ -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 {
@@ -3560,4 +3565,97 @@ mod tests {
3560
3565
) ,
3561
3566
}
3562
3567
}
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
+ }
3563
3661
}
0 commit comments