@@ -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
@@ -633,6 +635,8 @@ struct InvoiceFields {
633
635
fallbacks : Option < Vec < FallbackAddress > > ,
634
636
features : Bolt12InvoiceFeatures ,
635
637
signing_pubkey : PublicKey ,
638
+ #[ cfg( test) ]
639
+ experimental_baz : Option < u64 > ,
636
640
}
637
641
638
642
macro_rules! invoice_accessors { ( $self: ident, $contents: expr) => {
@@ -1216,7 +1220,10 @@ impl InvoiceFields {
1216
1220
node_id : Some ( & self . signing_pubkey ) ,
1217
1221
message_paths : None ,
1218
1222
} ,
1219
- ExperimentalInvoiceTlvStreamRef { } ,
1223
+ ExperimentalInvoiceTlvStreamRef {
1224
+ #[ cfg( test) ]
1225
+ experimental_baz : self . experimental_baz ,
1226
+ } ,
1220
1227
)
1221
1228
}
1222
1229
}
@@ -1305,12 +1312,20 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
1305
1312
} ) ;
1306
1313
1307
1314
/// Valid type range for experimental invoice TLV records.
1308
- const EXPERIMENTAL_INVOICE_TYPES : core:: ops:: RangeFrom < u64 > = 3_000_000_000 ..;
1315
+ pub ( super ) const EXPERIMENTAL_INVOICE_TYPES : core:: ops:: RangeFrom < u64 > = 3_000_000_000 ..;
1309
1316
1317
+ #[ cfg( not( test) ) ]
1310
1318
tlv_stream ! (
1311
1319
ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , { }
1312
1320
) ;
1313
1321
1322
+ #[ cfg( test) ]
1323
+ tlv_stream ! (
1324
+ ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , {
1325
+ ( 3_999_999_999 , experimental_baz: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
1326
+ }
1327
+ ) ;
1328
+
1314
1329
pub ( super ) type BlindedPathIter < ' a > = core:: iter:: Map <
1315
1330
core:: slice:: Iter < ' a , BlindedPaymentPath > ,
1316
1331
for <' r > fn ( & ' r BlindedPaymentPath ) -> & ' r BlindedPath ,
@@ -1445,7 +1460,10 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
1445
1460
} ,
1446
1461
experimental_offer_tlv_stream,
1447
1462
experimental_invoice_request_tlv_stream,
1448
- ExperimentalInvoiceTlvStream { } ,
1463
+ ExperimentalInvoiceTlvStream {
1464
+ #[ cfg( test) ]
1465
+ experimental_baz,
1466
+ } ,
1449
1467
) = tlv_stream;
1450
1468
1451
1469
if message_paths. is_some ( ) { return Err ( Bolt12SemanticError :: UnexpectedPaths ) }
@@ -1472,6 +1490,8 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
1472
1490
let fields = InvoiceFields {
1473
1491
payment_paths, created_at, relative_expiry, payment_hash, amount_msats, fallbacks,
1474
1492
features, signing_pubkey,
1493
+ #[ cfg( test) ]
1494
+ experimental_baz,
1475
1495
} ;
1476
1496
1477
1497
check_invoice_signing_pubkey ( & fields. signing_pubkey , & offer_tlv_stream) ?;
@@ -1542,7 +1562,7 @@ pub(super) fn check_invoice_signing_pubkey(
1542
1562
1543
1563
#[ cfg( test) ]
1544
1564
mod tests {
1545
- use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , ExperimentalInvoiceTlvStreamRef , FallbackAddress , FullInvoiceTlvStreamRef , INVOICE_TYPES , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
1565
+ use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , EXPERIMENTAL_INVOICE_TYPES , ExperimentalInvoiceTlvStreamRef , FallbackAddress , FullInvoiceTlvStreamRef , INVOICE_TYPES , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
1546
1566
1547
1567
use bitcoin:: { CompressedPublicKey , WitnessProgram , WitnessVersion } ;
1548
1568
use bitcoin:: constants:: ChainHash ;
@@ -1562,7 +1582,7 @@ mod tests {
1562
1582
use crate :: ln:: inbound_payment:: ExpandedKey ;
1563
1583
use crate :: ln:: msgs:: DecodeError ;
1564
1584
use crate :: offers:: invoice_request:: { ExperimentalInvoiceRequestTlvStreamRef , InvoiceRequestTlvStreamRef } ;
1565
- use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , TaggedHash , self } ;
1585
+ use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , TaggedHash , TlvStream , self } ;
1566
1586
use crate :: offers:: nonce:: Nonce ;
1567
1587
use crate :: offers:: offer:: { Amount , ExperimentalOfferTlvStreamRef , OfferTlvStreamRef , Quantity } ;
1568
1588
use crate :: prelude:: * ;
@@ -1738,7 +1758,9 @@ mod tests {
1738
1758
ExperimentalInvoiceRequestTlvStreamRef {
1739
1759
experimental_bar: None ,
1740
1760
} ,
1741
- ExperimentalInvoiceTlvStreamRef { } ,
1761
+ ExperimentalInvoiceTlvStreamRef {
1762
+ experimental_baz: None ,
1763
+ } ,
1742
1764
) ,
1743
1765
) ;
1744
1766
@@ -1838,7 +1860,9 @@ mod tests {
1838
1860
ExperimentalInvoiceRequestTlvStreamRef {
1839
1861
experimental_bar: None ,
1840
1862
} ,
1841
- ExperimentalInvoiceTlvStreamRef { } ,
1863
+ ExperimentalInvoiceTlvStreamRef {
1864
+ experimental_baz: None ,
1865
+ } ,
1842
1866
) ,
1843
1867
) ;
1844
1868
@@ -2685,6 +2709,97 @@ mod tests {
2685
2709
}
2686
2710
}
2687
2711
2712
+ #[ test]
2713
+ fn parses_invoice_with_experimental_tlv_records ( ) {
2714
+ let secp_ctx = Secp256k1 :: new ( ) ;
2715
+ let keys = Keypair :: from_secret_key ( & secp_ctx, & SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ) ;
2716
+ let invoice = OfferBuilder :: new ( keys. public_key ( ) )
2717
+ . amount_msats ( 1000 )
2718
+ . build ( ) . unwrap ( )
2719
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2720
+ . build ( ) . unwrap ( )
2721
+ . sign ( payer_sign) . unwrap ( )
2722
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2723
+ . experimental_baz ( 42 )
2724
+ . build ( ) . unwrap ( )
2725
+ . sign ( |message : & UnsignedBolt12Invoice |
2726
+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2727
+ )
2728
+ . unwrap ( ) ;
2729
+
2730
+ let mut buffer = Vec :: new ( ) ;
2731
+ invoice. write ( & mut buffer) . unwrap ( ) ;
2732
+
2733
+ assert ! ( Bolt12Invoice :: try_from( buffer) . is_ok( ) ) ;
2734
+
2735
+ const UNKNOWN_ODD_TYPE : u64 = EXPERIMENTAL_INVOICE_TYPES . start + 1 ;
2736
+ assert ! ( UNKNOWN_ODD_TYPE % 2 == 1 ) ;
2737
+
2738
+ let mut unsigned_invoice = OfferBuilder :: new ( keys. public_key ( ) )
2739
+ . amount_msats ( 1000 )
2740
+ . build ( ) . unwrap ( )
2741
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2742
+ . build ( ) . unwrap ( )
2743
+ . sign ( payer_sign) . unwrap ( )
2744
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2745
+ . build ( ) . unwrap ( ) ;
2746
+
2747
+ BigSize ( UNKNOWN_ODD_TYPE ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2748
+ BigSize ( 32 ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2749
+ [ 42u8 ; 32 ] . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2750
+
2751
+ let tlv_stream = TlvStream :: new ( & unsigned_invoice. bytes )
2752
+ . chain ( TlvStream :: new ( & unsigned_invoice. experimental_bytes ) ) ;
2753
+ unsigned_invoice. tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
2754
+
2755
+ let invoice = unsigned_invoice
2756
+ . sign ( |message : & UnsignedBolt12Invoice |
2757
+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2758
+ )
2759
+ . unwrap ( ) ;
2760
+
2761
+ let mut encoded_invoice = Vec :: new ( ) ;
2762
+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2763
+
2764
+ if let Err ( e) = Bolt12Invoice :: try_from ( encoded_invoice) {
2765
+ panic ! ( "error parsing invoice: {:?}" , e) ;
2766
+ }
2767
+
2768
+ const UNKNOWN_EVEN_TYPE : u64 = EXPERIMENTAL_INVOICE_TYPES . start ;
2769
+ assert ! ( UNKNOWN_EVEN_TYPE % 2 == 0 ) ;
2770
+
2771
+ let mut unsigned_invoice = OfferBuilder :: new ( keys. public_key ( ) )
2772
+ . amount_msats ( 1000 )
2773
+ . build ( ) . unwrap ( )
2774
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2775
+ . build ( ) . unwrap ( )
2776
+ . sign ( payer_sign) . unwrap ( )
2777
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2778
+ . build ( ) . unwrap ( ) ;
2779
+
2780
+ BigSize ( UNKNOWN_EVEN_TYPE ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2781
+ BigSize ( 32 ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2782
+ [ 42u8 ; 32 ] . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2783
+
2784
+ let tlv_stream = TlvStream :: new ( & unsigned_invoice. bytes )
2785
+ . chain ( TlvStream :: new ( & unsigned_invoice. experimental_bytes ) ) ;
2786
+ unsigned_invoice. tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
2787
+
2788
+ let invoice = unsigned_invoice
2789
+ . sign ( |message : & UnsignedBolt12Invoice |
2790
+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2791
+ )
2792
+ . unwrap ( ) ;
2793
+
2794
+ let mut encoded_invoice = Vec :: new ( ) ;
2795
+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2796
+
2797
+ match Bolt12Invoice :: try_from ( encoded_invoice) {
2798
+ Ok ( _) => panic ! ( "expected error" ) ,
2799
+ Err ( e) => assert_eq ! ( e, Bolt12ParseError :: Decode ( DecodeError :: UnknownRequiredFeature ) ) ,
2800
+ }
2801
+ }
2802
+
2688
2803
#[ test]
2689
2804
fn fails_parsing_invoice_with_out_of_range_tlv_records ( ) {
2690
2805
let invoice = OfferBuilder :: new ( recipient_pubkey ( ) )
0 commit comments