@@ -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
@@ -634,6 +636,8 @@ struct InvoiceFields {
634
636
fallbacks : Option < Vec < FallbackAddress > > ,
635
637
features : Bolt12InvoiceFeatures ,
636
638
signing_pubkey : PublicKey ,
639
+ #[ cfg( test) ]
640
+ experimental_baz : Option < u64 > ,
637
641
}
638
642
639
643
macro_rules! invoice_accessors { ( $self: ident, $contents: expr) => {
@@ -1217,7 +1221,10 @@ impl InvoiceFields {
1217
1221
node_id : Some ( & self . signing_pubkey ) ,
1218
1222
message_paths : None ,
1219
1223
} ,
1220
- ExperimentalInvoiceTlvStreamRef { } ,
1224
+ ExperimentalInvoiceTlvStreamRef {
1225
+ #[ cfg( test) ]
1226
+ experimental_baz : self . experimental_baz ,
1227
+ } ,
1221
1228
)
1222
1229
}
1223
1230
}
@@ -1294,12 +1301,20 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
1294
1301
} ) ;
1295
1302
1296
1303
/// Valid type range for experimental invoice TLV records.
1297
- const EXPERIMENTAL_INVOICE_TYPES : core:: ops:: RangeFrom < u64 > = 3_000_000_000 ..;
1304
+ pub ( super ) const EXPERIMENTAL_INVOICE_TYPES : core:: ops:: RangeFrom < u64 > = 3_000_000_000 ..;
1298
1305
1306
+ #[ cfg( not( test) ) ]
1299
1307
tlv_stream ! (
1300
1308
ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , { }
1301
1309
) ;
1302
1310
1311
+ #[ cfg( test) ]
1312
+ tlv_stream ! (
1313
+ ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , {
1314
+ ( 3_999_999_999 , experimental_baz: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
1315
+ }
1316
+ ) ;
1317
+
1303
1318
pub ( super ) type BlindedPathIter < ' a > = core:: iter:: Map <
1304
1319
core:: slice:: Iter < ' a , BlindedPaymentPath > ,
1305
1320
for <' r > fn ( & ' r BlindedPaymentPath ) -> & ' r BlindedPath ,
@@ -1434,7 +1449,10 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
1434
1449
} ,
1435
1450
experimental_offer_tlv_stream,
1436
1451
experimental_invoice_request_tlv_stream,
1437
- ExperimentalInvoiceTlvStream { } ,
1452
+ ExperimentalInvoiceTlvStream {
1453
+ #[ cfg( test) ]
1454
+ experimental_baz,
1455
+ } ,
1438
1456
) = tlv_stream;
1439
1457
1440
1458
if message_paths. is_some ( ) { return Err ( Bolt12SemanticError :: UnexpectedPaths ) }
@@ -1461,6 +1479,8 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
1461
1479
let fields = InvoiceFields {
1462
1480
payment_paths, created_at, relative_expiry, payment_hash, amount_msats, fallbacks,
1463
1481
features, signing_pubkey,
1482
+ #[ cfg( test) ]
1483
+ experimental_baz,
1464
1484
} ;
1465
1485
1466
1486
check_invoice_signing_pubkey ( & fields. signing_pubkey , & offer_tlv_stream) ?;
@@ -1531,7 +1551,7 @@ pub(super) fn check_invoice_signing_pubkey(
1531
1551
1532
1552
#[ cfg( test) ]
1533
1553
mod tests {
1534
- use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , ExperimentalInvoiceTlvStreamRef , FallbackAddress , FullInvoiceTlvStreamRef , INVOICE_TYPES , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
1554
+ use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , EXPERIMENTAL_INVOICE_TYPES , ExperimentalInvoiceTlvStreamRef , FallbackAddress , FullInvoiceTlvStreamRef , INVOICE_TYPES , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
1535
1555
1536
1556
use bitcoin:: { CompressedPublicKey , WitnessProgram , WitnessVersion } ;
1537
1557
use bitcoin:: constants:: ChainHash ;
@@ -1551,7 +1571,7 @@ mod tests {
1551
1571
use crate :: ln:: inbound_payment:: ExpandedKey ;
1552
1572
use crate :: ln:: msgs:: DecodeError ;
1553
1573
use crate :: offers:: invoice_request:: { ExperimentalInvoiceRequestTlvStreamRef , InvoiceRequestTlvStreamRef } ;
1554
- use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , TaggedHash , self } ;
1574
+ use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , TaggedHash , TlvStream , self } ;
1555
1575
use crate :: offers:: nonce:: Nonce ;
1556
1576
use crate :: offers:: offer:: { Amount , ExperimentalOfferTlvStreamRef , OfferTlvStreamRef , Quantity } ;
1557
1577
use crate :: prelude:: * ;
@@ -1727,7 +1747,9 @@ mod tests {
1727
1747
ExperimentalInvoiceRequestTlvStreamRef {
1728
1748
experimental_bar: None ,
1729
1749
} ,
1730
- ExperimentalInvoiceTlvStreamRef { } ,
1750
+ ExperimentalInvoiceTlvStreamRef {
1751
+ experimental_baz: None ,
1752
+ } ,
1731
1753
) ,
1732
1754
) ;
1733
1755
@@ -1827,7 +1849,9 @@ mod tests {
1827
1849
ExperimentalInvoiceRequestTlvStreamRef {
1828
1850
experimental_bar: None ,
1829
1851
} ,
1830
- ExperimentalInvoiceTlvStreamRef { } ,
1852
+ ExperimentalInvoiceTlvStreamRef {
1853
+ experimental_baz: None ,
1854
+ } ,
1831
1855
) ,
1832
1856
) ;
1833
1857
@@ -2674,6 +2698,97 @@ mod tests {
2674
2698
}
2675
2699
}
2676
2700
2701
+ #[ test]
2702
+ fn parses_invoice_with_experimental_tlv_records ( ) {
2703
+ let secp_ctx = Secp256k1 :: new ( ) ;
2704
+ let keys = Keypair :: from_secret_key ( & secp_ctx, & SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ) ;
2705
+ let invoice = OfferBuilder :: new ( keys. public_key ( ) )
2706
+ . amount_msats ( 1000 )
2707
+ . build ( ) . unwrap ( )
2708
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2709
+ . build ( ) . unwrap ( )
2710
+ . sign ( payer_sign) . unwrap ( )
2711
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2712
+ . experimental_baz ( 42 )
2713
+ . build ( ) . unwrap ( )
2714
+ . sign ( |message : & UnsignedBolt12Invoice |
2715
+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2716
+ )
2717
+ . unwrap ( ) ;
2718
+
2719
+ let mut buffer = Vec :: new ( ) ;
2720
+ invoice. write ( & mut buffer) . unwrap ( ) ;
2721
+
2722
+ assert ! ( Bolt12Invoice :: try_from( buffer) . is_ok( ) ) ;
2723
+
2724
+ const UNKNOWN_ODD_TYPE : u64 = EXPERIMENTAL_INVOICE_TYPES . start + 1 ;
2725
+ assert ! ( UNKNOWN_ODD_TYPE % 2 == 1 ) ;
2726
+
2727
+ let mut unsigned_invoice = OfferBuilder :: new ( keys. public_key ( ) )
2728
+ . amount_msats ( 1000 )
2729
+ . build ( ) . unwrap ( )
2730
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2731
+ . build ( ) . unwrap ( )
2732
+ . sign ( payer_sign) . unwrap ( )
2733
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2734
+ . build ( ) . unwrap ( ) ;
2735
+
2736
+ BigSize ( UNKNOWN_ODD_TYPE ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2737
+ BigSize ( 32 ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2738
+ [ 42u8 ; 32 ] . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2739
+
2740
+ let tlv_stream = TlvStream :: new ( & unsigned_invoice. bytes )
2741
+ . chain ( TlvStream :: new ( & unsigned_invoice. experimental_bytes ) ) ;
2742
+ unsigned_invoice. tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
2743
+
2744
+ let invoice = unsigned_invoice
2745
+ . sign ( |message : & UnsignedBolt12Invoice |
2746
+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2747
+ )
2748
+ . unwrap ( ) ;
2749
+
2750
+ let mut encoded_invoice = Vec :: new ( ) ;
2751
+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2752
+
2753
+ if let Err ( e) = Bolt12Invoice :: try_from ( encoded_invoice) {
2754
+ panic ! ( "error parsing invoice: {:?}" , e) ;
2755
+ }
2756
+
2757
+ const UNKNOWN_EVEN_TYPE : u64 = EXPERIMENTAL_INVOICE_TYPES . start ;
2758
+ assert ! ( UNKNOWN_EVEN_TYPE % 2 == 0 ) ;
2759
+
2760
+ let mut unsigned_invoice = OfferBuilder :: new ( keys. public_key ( ) )
2761
+ . amount_msats ( 1000 )
2762
+ . build ( ) . unwrap ( )
2763
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2764
+ . build ( ) . unwrap ( )
2765
+ . sign ( payer_sign) . unwrap ( )
2766
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2767
+ . build ( ) . unwrap ( ) ;
2768
+
2769
+ BigSize ( UNKNOWN_EVEN_TYPE ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2770
+ BigSize ( 32 ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2771
+ [ 42u8 ; 32 ] . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2772
+
2773
+ let tlv_stream = TlvStream :: new ( & unsigned_invoice. bytes )
2774
+ . chain ( TlvStream :: new ( & unsigned_invoice. experimental_bytes ) ) ;
2775
+ unsigned_invoice. tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
2776
+
2777
+ let invoice = unsigned_invoice
2778
+ . sign ( |message : & UnsignedBolt12Invoice |
2779
+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2780
+ )
2781
+ . unwrap ( ) ;
2782
+
2783
+ let mut encoded_invoice = Vec :: new ( ) ;
2784
+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2785
+
2786
+ match Bolt12Invoice :: try_from ( encoded_invoice) {
2787
+ Ok ( _) => panic ! ( "expected error" ) ,
2788
+ Err ( e) => assert_eq ! ( e, Bolt12ParseError :: Decode ( DecodeError :: UnknownRequiredFeature ) ) ,
2789
+ }
2790
+ }
2791
+
2677
2792
#[ test]
2678
2793
fn fails_parsing_invoice_with_out_of_range_tlv_records ( ) {
2679
2794
let invoice = OfferBuilder :: new ( recipient_pubkey ( ) )
0 commit comments