@@ -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 {
@@ -1777,6 +1782,14 @@ mod tests {
1777
1782
EXPERIMENTAL_INVOICE_TYPES , INVOICE_TYPES , SIGNATURE_TAG ,
1778
1783
} ;
1779
1784
1785
+ use crate :: ln:: channelmanager:: PaymentId ;
1786
+ use crate :: ln:: inbound_payment:: ExpandedKey ;
1787
+ use crate :: offers:: merkle;
1788
+ use crate :: offers:: nonce:: Nonce ;
1789
+ use crate :: offers:: offer:: OfferBuilder ;
1790
+ use crate :: offers:: test_utils:: {
1791
+ payment_hash, payment_paths, recipient_pubkey, recipient_sign, FixedEntropy ,
1792
+ } ;
1780
1793
use bitcoin:: address:: Address ;
1781
1794
use bitcoin:: constants:: ChainHash ;
1782
1795
use bitcoin:: hashes:: Hash ;
@@ -1785,37 +1798,8 @@ mod tests {
1785
1798
use bitcoin:: script:: ScriptBuf ;
1786
1799
use bitcoin:: secp256k1:: { self , Keypair , Message , Secp256k1 , SecretKey , XOnlyPublicKey } ;
1787
1800
use bitcoin:: { CompressedPublicKey , WitnessProgram , WitnessVersion } ;
1788
-
1789
1801
use core:: time:: Duration ;
1790
1802
1791
- use crate :: blinded_path:: message:: BlindedMessagePath ;
1792
- use crate :: blinded_path:: BlindedHop ;
1793
- use crate :: ln:: channelmanager:: PaymentId ;
1794
- use crate :: ln:: inbound_payment:: ExpandedKey ;
1795
- use crate :: ln:: msgs:: DecodeError ;
1796
- use crate :: offers:: invoice_request:: {
1797
- ExperimentalInvoiceRequestTlvStreamRef , InvoiceRequestTlvStreamRef ,
1798
- } ;
1799
- use crate :: offers:: merkle:: { self , SignError , SignatureTlvStreamRef , TaggedHash , TlvStream } ;
1800
- use crate :: offers:: nonce:: Nonce ;
1801
- use crate :: offers:: offer:: {
1802
- Amount , ExperimentalOfferTlvStreamRef , OfferTlvStreamRef , Quantity ,
1803
- } ;
1804
- use crate :: offers:: parse:: { Bolt12ParseError , Bolt12SemanticError } ;
1805
- use crate :: offers:: payer:: PayerTlvStreamRef ;
1806
- use crate :: offers:: test_utils:: * ;
1807
- use crate :: prelude:: * ;
1808
- use crate :: types:: features:: { Bolt12InvoiceFeatures , InvoiceRequestFeatures , OfferFeatures } ;
1809
- use crate :: types:: string:: PrintableString ;
1810
- use crate :: util:: ser:: { BigSize , Iterable , Writeable } ;
1811
- #[ cfg( not( c_bindings) ) ]
1812
- use { crate :: offers:: offer:: OfferBuilder , crate :: offers:: refund:: RefundBuilder } ;
1813
- #[ cfg( c_bindings) ]
1814
- use {
1815
- crate :: offers:: offer:: OfferWithExplicitMetadataBuilder as OfferBuilder ,
1816
- crate :: offers:: refund:: RefundMaybeWithDerivedMetadataBuilder as RefundBuilder ,
1817
- } ;
1818
-
1819
1803
trait ToBytes {
1820
1804
fn to_bytes ( & self ) -> Vec < u8 > ;
1821
1805
}
@@ -3560,4 +3544,86 @@ mod tests {
3560
3544
) ,
3561
3545
}
3562
3546
}
3547
+
3548
+ #[ test]
3549
+ fn invoice_offer_id_matches_offer_id ( ) {
3550
+ let expanded_key = ExpandedKey :: new ( [ 42 ; 32 ] ) ;
3551
+ let entropy = FixedEntropy { } ;
3552
+ let nonce = Nonce :: from_entropy_source ( & entropy) ;
3553
+ let secp_ctx = Secp256k1 :: new ( ) ;
3554
+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
3555
+
3556
+ let offer = OfferBuilder :: new ( recipient_pubkey ( ) ) . amount_msats ( 1000 ) . build ( ) . unwrap ( ) ;
3557
+
3558
+ let offer_id = offer. id ( ) ;
3559
+
3560
+ let invoice_request = offer
3561
+ . request_invoice ( & expanded_key, nonce, & secp_ctx, payment_id)
3562
+ . unwrap ( )
3563
+ . build_and_sign ( )
3564
+ . unwrap ( ) ;
3565
+
3566
+ let invoice = invoice_request
3567
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) )
3568
+ . unwrap ( )
3569
+ . build ( )
3570
+ . unwrap ( )
3571
+ . sign ( recipient_sign)
3572
+ . unwrap ( ) ;
3573
+
3574
+ assert_eq ! ( invoice. offer_id( ) , Some ( offer_id) ) ;
3575
+ }
3576
+
3577
+ #[ test]
3578
+ fn refund_invoice_has_no_offer_id ( ) {
3579
+ let refund =
3580
+ RefundBuilder :: new ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) , 1000 ) . unwrap ( ) . build ( ) . unwrap ( ) ;
3581
+
3582
+ let invoice = refund
3583
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , recipient_pubkey ( ) , now ( ) )
3584
+ . unwrap ( )
3585
+ . build ( )
3586
+ . unwrap ( )
3587
+ . sign ( recipient_sign)
3588
+ . unwrap ( ) ;
3589
+
3590
+ assert_eq ! ( invoice. offer_id( ) , None ) ;
3591
+ }
3592
+
3593
+ #[ test]
3594
+ fn verifies_invoice_signature_with_tagged_hash ( ) {
3595
+ let secp_ctx = Secp256k1 :: new ( ) ;
3596
+ let expanded_key = ExpandedKey :: new ( [ 42 ; 32 ] ) ;
3597
+ let entropy = FixedEntropy { } ;
3598
+ let nonce = Nonce :: from_entropy_source ( & entropy) ;
3599
+ let node_id = recipient_pubkey ( ) ;
3600
+ let payment_paths = payment_paths ( ) ;
3601
+ let now = Duration :: from_secs ( 123456 ) ;
3602
+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
3603
+
3604
+ let offer = OfferBuilder :: new ( node_id)
3605
+ . amount_msats ( 1000 )
3606
+ . path ( crate :: offers:: test_utils:: blinded_path ( ) )
3607
+ . build ( )
3608
+ . unwrap ( ) ;
3609
+
3610
+ let invoice_request = offer
3611
+ . request_invoice ( & expanded_key, nonce, & secp_ctx, payment_id)
3612
+ . unwrap ( )
3613
+ . build_and_sign ( )
3614
+ . unwrap ( ) ;
3615
+
3616
+ let invoice = invoice_request
3617
+ . respond_with_no_std ( payment_paths, payment_hash ( ) , now)
3618
+ . unwrap ( )
3619
+ . build ( )
3620
+ . unwrap ( )
3621
+ . sign ( recipient_sign)
3622
+ . unwrap ( ) ;
3623
+
3624
+ let issuer_sign_pubkey = offer. issuer_signing_pubkey ( ) . unwrap ( ) ;
3625
+ let tagged_hash = invoice. tagged_hash ( ) ;
3626
+ let signature = invoice. signature ( ) ;
3627
+ assert ! ( merkle:: verify_signature( & signature, tagged_hash, issuer_sign_pubkey) . is_ok( ) ) ;
3628
+ }
3563
3629
}
0 commit comments