@@ -363,6 +363,8 @@ macro_rules! invoice_builder_methods { (
363
363
InvoiceFields {
364
364
payment_paths, created_at, relative_expiry: None , payment_hash, amount_msats,
365
365
fallbacks: None , features: Bolt12InvoiceFeatures :: empty( ) , signing_pubkey,
366
+ #[ cfg( test) ]
367
+ experimental_baz: None ,
366
368
}
367
369
}
368
370
@@ -666,6 +668,8 @@ struct InvoiceFields {
666
668
fallbacks : Option < Vec < FallbackAddress > > ,
667
669
features : Bolt12InvoiceFeatures ,
668
670
signing_pubkey : PublicKey ,
671
+ #[ cfg( test) ]
672
+ experimental_baz : Option < u64 > ,
669
673
}
670
674
671
675
macro_rules! invoice_accessors { ( $self: ident, $contents: expr) => {
@@ -1256,7 +1260,10 @@ impl InvoiceFields {
1256
1260
node_id : Some ( & self . signing_pubkey ) ,
1257
1261
message_paths : None ,
1258
1262
} ,
1259
- ExperimentalInvoiceTlvStreamRef { } ,
1263
+ ExperimentalInvoiceTlvStreamRef {
1264
+ #[ cfg( test) ]
1265
+ experimental_baz : self . experimental_baz ,
1266
+ } ,
1260
1267
)
1261
1268
}
1262
1269
}
@@ -1333,12 +1340,20 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
1333
1340
} ) ;
1334
1341
1335
1342
/// Valid type range for experimental invoice TLV records.
1336
- const EXPERIMENTAL_INVOICE_TYPES : core:: ops:: RangeFrom < u64 > = 3_000_000_000 ..;
1343
+ pub ( super ) const EXPERIMENTAL_INVOICE_TYPES : core:: ops:: RangeFrom < u64 > = 3_000_000_000 ..;
1337
1344
1345
+ #[ cfg( not( test) ) ]
1338
1346
tlv_stream ! (
1339
1347
ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , { }
1340
1348
) ;
1341
1349
1350
+ #[ cfg( test) ]
1351
+ tlv_stream ! (
1352
+ ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , {
1353
+ ( 3_999_999_999 , experimental_baz: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
1354
+ }
1355
+ ) ;
1356
+
1342
1357
pub ( super ) type BlindedPathIter < ' a > = core:: iter:: Map <
1343
1358
core:: slice:: Iter < ' a , BlindedPaymentPath > ,
1344
1359
for <' r > fn ( & ' r BlindedPaymentPath ) -> & ' r BlindedPath ,
@@ -1473,7 +1488,10 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
1473
1488
} ,
1474
1489
experimental_offer_tlv_stream,
1475
1490
experimental_invoice_request_tlv_stream,
1476
- ExperimentalInvoiceTlvStream { } ,
1491
+ ExperimentalInvoiceTlvStream {
1492
+ #[ cfg( test) ]
1493
+ experimental_baz,
1494
+ } ,
1477
1495
) = tlv_stream;
1478
1496
1479
1497
if message_paths. is_some ( ) { return Err ( Bolt12SemanticError :: UnexpectedPaths ) }
@@ -1500,6 +1518,8 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
1500
1518
let fields = InvoiceFields {
1501
1519
payment_paths, created_at, relative_expiry, payment_hash, amount_msats, fallbacks,
1502
1520
features, signing_pubkey,
1521
+ #[ cfg( test) ]
1522
+ experimental_baz,
1503
1523
} ;
1504
1524
1505
1525
check_invoice_signing_pubkey ( & fields. signing_pubkey , & offer_tlv_stream) ?;
@@ -1570,7 +1590,7 @@ pub(super) fn check_invoice_signing_pubkey(
1570
1590
1571
1591
#[ cfg( test) ]
1572
1592
mod tests {
1573
- use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , ExperimentalInvoiceTlvStreamRef , FallbackAddress , FullInvoiceTlvStreamRef , INVOICE_TYPES , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
1593
+ use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , EXPERIMENTAL_INVOICE_TYPES , ExperimentalInvoiceTlvStreamRef , FallbackAddress , FullInvoiceTlvStreamRef , INVOICE_TYPES , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
1574
1594
1575
1595
use bitcoin:: { CompressedPublicKey , WitnessProgram , WitnessVersion } ;
1576
1596
use bitcoin:: constants:: ChainHash ;
@@ -1590,7 +1610,7 @@ mod tests {
1590
1610
use crate :: ln:: inbound_payment:: ExpandedKey ;
1591
1611
use crate :: ln:: msgs:: DecodeError ;
1592
1612
use crate :: offers:: invoice_request:: { ExperimentalInvoiceRequestTlvStreamRef , InvoiceRequestTlvStreamRef } ;
1593
- use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , TaggedHash , self } ;
1613
+ use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , TaggedHash , TlvStream , self } ;
1594
1614
use crate :: offers:: nonce:: Nonce ;
1595
1615
use crate :: offers:: offer:: { Amount , ExperimentalOfferTlvStreamRef , OfferTlvStreamRef , Quantity } ;
1596
1616
use crate :: prelude:: * ;
@@ -1766,7 +1786,9 @@ mod tests {
1766
1786
ExperimentalInvoiceRequestTlvStreamRef {
1767
1787
experimental_bar: None ,
1768
1788
} ,
1769
- ExperimentalInvoiceTlvStreamRef { } ,
1789
+ ExperimentalInvoiceTlvStreamRef {
1790
+ experimental_baz: None ,
1791
+ } ,
1770
1792
) ,
1771
1793
) ;
1772
1794
@@ -1866,7 +1888,9 @@ mod tests {
1866
1888
ExperimentalInvoiceRequestTlvStreamRef {
1867
1889
experimental_bar: None ,
1868
1890
} ,
1869
- ExperimentalInvoiceTlvStreamRef { } ,
1891
+ ExperimentalInvoiceTlvStreamRef {
1892
+ experimental_baz: None ,
1893
+ } ,
1870
1894
) ,
1871
1895
) ;
1872
1896
@@ -2724,6 +2748,135 @@ mod tests {
2724
2748
}
2725
2749
}
2726
2750
2751
+ #[ test]
2752
+ fn parses_invoice_with_experimental_tlv_records ( ) {
2753
+ let secp_ctx = Secp256k1 :: new ( ) ;
2754
+ let keys = Keypair :: from_secret_key ( & secp_ctx, & SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ) ;
2755
+ let invoice = OfferBuilder :: new ( keys. public_key ( ) )
2756
+ . amount_msats ( 1000 )
2757
+ . build ( ) . unwrap ( )
2758
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2759
+ . build ( ) . unwrap ( )
2760
+ . sign ( payer_sign) . unwrap ( )
2761
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2762
+ . experimental_baz ( 42 )
2763
+ . build ( ) . unwrap ( )
2764
+ . sign ( |message : & UnsignedBolt12Invoice |
2765
+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2766
+ )
2767
+ . unwrap ( ) ;
2768
+
2769
+ let mut encoded_invoice = Vec :: new ( ) ;
2770
+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2771
+
2772
+ assert ! ( Bolt12Invoice :: try_from( encoded_invoice) . is_ok( ) ) ;
2773
+
2774
+ const UNKNOWN_ODD_TYPE : u64 = EXPERIMENTAL_INVOICE_TYPES . start + 1 ;
2775
+ assert ! ( UNKNOWN_ODD_TYPE % 2 == 1 ) ;
2776
+
2777
+ let mut unsigned_invoice = OfferBuilder :: new ( keys. public_key ( ) )
2778
+ . amount_msats ( 1000 )
2779
+ . build ( ) . unwrap ( )
2780
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2781
+ . build ( ) . unwrap ( )
2782
+ . sign ( payer_sign) . unwrap ( )
2783
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2784
+ . build ( ) . unwrap ( ) ;
2785
+
2786
+ let mut unknown_bytes = Vec :: new ( ) ;
2787
+ BigSize ( UNKNOWN_ODD_TYPE ) . write ( & mut unknown_bytes) . unwrap ( ) ;
2788
+ BigSize ( 32 ) . write ( & mut unknown_bytes) . unwrap ( ) ;
2789
+ [ 42u8 ; 32 ] . write ( & mut unknown_bytes) . unwrap ( ) ;
2790
+
2791
+ unsigned_invoice. bytes . reserve_exact (
2792
+ unsigned_invoice. bytes . capacity ( ) - unsigned_invoice. bytes . len ( ) + unknown_bytes. len ( ) ,
2793
+ ) ;
2794
+ unsigned_invoice. experimental_bytes . extend_from_slice ( & unknown_bytes) ;
2795
+
2796
+ let tlv_stream = TlvStream :: new ( & unsigned_invoice. bytes )
2797
+ . chain ( TlvStream :: new ( & unsigned_invoice. experimental_bytes ) ) ;
2798
+ unsigned_invoice. tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
2799
+
2800
+ let invoice = unsigned_invoice
2801
+ . sign ( |message : & UnsignedBolt12Invoice |
2802
+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2803
+ )
2804
+ . unwrap ( ) ;
2805
+
2806
+ let mut encoded_invoice = Vec :: new ( ) ;
2807
+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2808
+
2809
+ match Bolt12Invoice :: try_from ( encoded_invoice. clone ( ) ) {
2810
+ Ok ( invoice) => assert_eq ! ( invoice. bytes, encoded_invoice) ,
2811
+ Err ( e) => panic ! ( "error parsing invoice: {:?}" , e) ,
2812
+ }
2813
+
2814
+ const UNKNOWN_EVEN_TYPE : u64 = EXPERIMENTAL_INVOICE_TYPES . start ;
2815
+ assert ! ( UNKNOWN_EVEN_TYPE % 2 == 0 ) ;
2816
+
2817
+ let mut unsigned_invoice = OfferBuilder :: new ( keys. public_key ( ) )
2818
+ . amount_msats ( 1000 )
2819
+ . build ( ) . unwrap ( )
2820
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2821
+ . build ( ) . unwrap ( )
2822
+ . sign ( payer_sign) . unwrap ( )
2823
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2824
+ . build ( ) . unwrap ( ) ;
2825
+
2826
+ let mut unknown_bytes = Vec :: new ( ) ;
2827
+ BigSize ( UNKNOWN_EVEN_TYPE ) . write ( & mut unknown_bytes) . unwrap ( ) ;
2828
+ BigSize ( 32 ) . write ( & mut unknown_bytes) . unwrap ( ) ;
2829
+ [ 42u8 ; 32 ] . write ( & mut unknown_bytes) . unwrap ( ) ;
2830
+
2831
+ unsigned_invoice. bytes . reserve_exact (
2832
+ unsigned_invoice. bytes . capacity ( ) - unsigned_invoice. bytes . len ( ) + unknown_bytes. len ( ) ,
2833
+ ) ;
2834
+ unsigned_invoice. experimental_bytes . extend_from_slice ( & unknown_bytes) ;
2835
+
2836
+ let tlv_stream = TlvStream :: new ( & unsigned_invoice. bytes )
2837
+ . chain ( TlvStream :: new ( & unsigned_invoice. experimental_bytes ) ) ;
2838
+ unsigned_invoice. tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
2839
+
2840
+ let invoice = unsigned_invoice
2841
+ . sign ( |message : & UnsignedBolt12Invoice |
2842
+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2843
+ )
2844
+ . unwrap ( ) ;
2845
+
2846
+ let mut encoded_invoice = Vec :: new ( ) ;
2847
+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2848
+
2849
+ match Bolt12Invoice :: try_from ( encoded_invoice) {
2850
+ Ok ( _) => panic ! ( "expected error" ) ,
2851
+ Err ( e) => assert_eq ! ( e, Bolt12ParseError :: Decode ( DecodeError :: UnknownRequiredFeature ) ) ,
2852
+ }
2853
+
2854
+ let invoice = OfferBuilder :: new ( keys. public_key ( ) )
2855
+ . amount_msats ( 1000 )
2856
+ . build ( ) . unwrap ( )
2857
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2858
+ . build ( ) . unwrap ( )
2859
+ . sign ( payer_sign) . unwrap ( )
2860
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2861
+ . build ( ) . unwrap ( )
2862
+ . sign ( |message : & UnsignedBolt12Invoice |
2863
+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2864
+ )
2865
+ . unwrap ( ) ;
2866
+
2867
+ let mut encoded_invoice = Vec :: new ( ) ;
2868
+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2869
+
2870
+ BigSize ( UNKNOWN_ODD_TYPE ) . write ( & mut encoded_invoice) . unwrap ( ) ;
2871
+ BigSize ( 32 ) . write ( & mut encoded_invoice) . unwrap ( ) ;
2872
+ [ 42u8 ; 32 ] . write ( & mut encoded_invoice) . unwrap ( ) ;
2873
+
2874
+ match Bolt12Invoice :: try_from ( encoded_invoice) {
2875
+ Ok ( _) => panic ! ( "expected error" ) ,
2876
+ Err ( e) => assert_eq ! ( e, Bolt12ParseError :: InvalidSignature ( secp256k1:: Error :: IncorrectSignature ) ) ,
2877
+ }
2878
+ }
2879
+
2727
2880
#[ test]
2728
2881
fn fails_parsing_invoice_with_out_of_range_tlv_records ( ) {
2729
2882
let invoice = OfferBuilder :: new ( recipient_pubkey ( ) )
0 commit comments