@@ -117,6 +117,8 @@ use crate::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures, InvoiceRequ
117
117
use crate :: ln:: inbound_payment:: { ExpandedKey , IV_LEN } ;
118
118
use crate :: ln:: msgs:: DecodeError ;
119
119
use crate :: offers:: invoice_macros:: { invoice_accessors_common, invoice_builder_methods_common} ;
120
+ #[ cfg( test) ]
121
+ use crate :: offers:: invoice_macros:: invoice_builder_test_methods;
120
122
use crate :: offers:: invoice_request:: { EXPERIMENTAL_INVOICE_REQUEST_TYPES , ExperimentalInvoiceRequestTlvStream , ExperimentalInvoiceRequestTlvStreamRef , INVOICE_REQUEST_PAYER_ID_TYPE , INVOICE_REQUEST_TYPES , IV_BYTES as INVOICE_REQUEST_IV_BYTES , InvoiceRequest , InvoiceRequestContents , InvoiceRequestTlvStream , InvoiceRequestTlvStreamRef } ;
121
123
use crate :: offers:: merkle:: { SignError , SignFn , SignatureTlvStream , SignatureTlvStreamRef , TaggedHash , TlvStream , self } ;
122
124
use crate :: offers:: nonce:: Nonce ;
@@ -359,6 +361,8 @@ macro_rules! invoice_builder_methods { (
359
361
InvoiceFields {
360
362
payment_paths, created_at, relative_expiry: None , payment_hash, amount_msats,
361
363
fallbacks: None , features: Bolt12InvoiceFeatures :: empty( ) , signing_pubkey,
364
+ #[ cfg( test) ]
365
+ experimental_baz: None ,
362
366
}
363
367
}
364
368
@@ -385,6 +389,9 @@ impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> {
385
389
impl < ' a , S : SigningPubkeyStrategy > InvoiceBuilder < ' a , S > {
386
390
invoice_builder_methods ! ( self , Self , Self , self , S , mut ) ;
387
391
invoice_builder_methods_common ! ( self , Self , self . invoice. fields_mut( ) , Self , self , S , Bolt12Invoice , mut ) ;
392
+
393
+ #[ cfg( test) ]
394
+ invoice_builder_test_methods ! ( self , Self , self . invoice. fields_mut( ) , Self , self , mut ) ;
388
395
}
389
396
390
397
#[ cfg( all( c_bindings, not( test) ) ) ]
@@ -399,6 +406,7 @@ impl<'a> InvoiceWithExplicitSigningPubkeyBuilder<'a> {
399
406
invoice_explicit_signing_pubkey_builder_methods ! ( self , & mut Self ) ;
400
407
invoice_builder_methods ! ( self , & mut Self , & mut Self , self , ExplicitSigningPubkey ) ;
401
408
invoice_builder_methods_common ! ( self , & mut Self , self . invoice. fields_mut( ) , & mut Self , self , ExplicitSigningPubkey , Bolt12Invoice ) ;
409
+ invoice_builder_test_methods ! ( self , & mut Self , self . invoice. fields_mut( ) , & mut Self , self ) ;
402
410
}
403
411
404
412
#[ cfg( all( c_bindings, not( test) ) ) ]
@@ -413,6 +421,7 @@ impl<'a> InvoiceWithDerivedSigningPubkeyBuilder<'a> {
413
421
invoice_derived_signing_pubkey_builder_methods ! ( self , & mut Self ) ;
414
422
invoice_builder_methods ! ( self , & mut Self , & mut Self , self , DerivedSigningPubkey ) ;
415
423
invoice_builder_methods_common ! ( self , & mut Self , self . invoice. fields_mut( ) , & mut Self , self , DerivedSigningPubkey , Bolt12Invoice ) ;
424
+ invoice_builder_test_methods ! ( self , & mut Self , self . invoice. fields_mut( ) , & mut Self , self ) ;
416
425
}
417
426
418
427
#[ cfg( c_bindings) ]
@@ -624,6 +633,8 @@ struct InvoiceFields {
624
633
fallbacks : Option < Vec < FallbackAddress > > ,
625
634
features : Bolt12InvoiceFeatures ,
626
635
signing_pubkey : PublicKey ,
636
+ #[ cfg( test) ]
637
+ experimental_baz : Option < u64 > ,
627
638
}
628
639
629
640
macro_rules! invoice_accessors { ( $self: ident, $contents: expr) => {
@@ -1208,7 +1219,10 @@ impl InvoiceFields {
1208
1219
node_id : Some ( & self . signing_pubkey ) ,
1209
1220
message_paths : None ,
1210
1221
} ,
1211
- ExperimentalInvoiceTlvStreamRef { } ,
1222
+ ExperimentalInvoiceTlvStreamRef {
1223
+ #[ cfg( test) ]
1224
+ experimental_baz : self . experimental_baz ,
1225
+ } ,
1212
1226
)
1213
1227
}
1214
1228
}
@@ -1297,12 +1311,20 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
1297
1311
} ) ;
1298
1312
1299
1313
/// Valid type range for experimental invoice TLV records.
1300
- const EXPERIMENTAL_INVOICE_TYPES : core:: ops:: RangeFrom < u64 > = 3_000_000_000 ..;
1314
+ pub ( super ) const EXPERIMENTAL_INVOICE_TYPES : core:: ops:: RangeFrom < u64 > = 3_000_000_000 ..;
1301
1315
1316
+ #[ cfg( not( test) ) ]
1302
1317
tlv_stream ! (
1303
1318
ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , { }
1304
1319
) ;
1305
1320
1321
+ #[ cfg( test) ]
1322
+ tlv_stream ! (
1323
+ ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , {
1324
+ ( 3_999_999_999 , experimental_baz: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
1325
+ }
1326
+ ) ;
1327
+
1306
1328
pub ( super ) type BlindedPathIter < ' a > = core:: iter:: Map <
1307
1329
core:: slice:: Iter < ' a , ( BlindedPayInfo , BlindedPath ) > ,
1308
1330
for <' r > fn ( & ' r ( BlindedPayInfo , BlindedPath ) ) -> & ' r BlindedPath ,
@@ -1475,7 +1497,10 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
1475
1497
} ,
1476
1498
experimental_offer_tlv_stream,
1477
1499
experimental_invoice_request_tlv_stream,
1478
- ExperimentalInvoiceTlvStream { } ,
1500
+ ExperimentalInvoiceTlvStream {
1501
+ #[ cfg( test) ]
1502
+ experimental_baz,
1503
+ } ,
1479
1504
) = tlv_stream;
1480
1505
1481
1506
if message_paths. is_some ( ) { return Err ( Bolt12SemanticError :: UnexpectedPaths ) }
@@ -1502,6 +1527,8 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
1502
1527
let fields = InvoiceFields {
1503
1528
payment_paths, created_at, relative_expiry, payment_hash, amount_msats, fallbacks,
1504
1529
features, signing_pubkey,
1530
+ #[ cfg( test) ]
1531
+ experimental_baz,
1505
1532
} ;
1506
1533
1507
1534
check_invoice_signing_pubkey ( & fields. signing_pubkey , & offer_tlv_stream) ?;
@@ -1567,7 +1594,7 @@ pub(super) fn check_invoice_signing_pubkey(
1567
1594
1568
1595
#[ cfg( test) ]
1569
1596
mod tests {
1570
- use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , ExperimentalInvoiceTlvStreamRef , FallbackAddress , FullInvoiceTlvStreamRef , INVOICE_TYPES , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
1597
+ use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , EXPERIMENTAL_INVOICE_TYPES , ExperimentalInvoiceTlvStreamRef , FallbackAddress , FullInvoiceTlvStreamRef , INVOICE_TYPES , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
1571
1598
1572
1599
use bitcoin:: { WitnessProgram , WitnessVersion } ;
1573
1600
use bitcoin:: blockdata:: constants:: ChainHash ;
@@ -1586,7 +1613,7 @@ mod tests {
1586
1613
use crate :: ln:: inbound_payment:: ExpandedKey ;
1587
1614
use crate :: ln:: msgs:: DecodeError ;
1588
1615
use crate :: offers:: invoice_request:: { ExperimentalInvoiceRequestTlvStreamRef , InvoiceRequestTlvStreamRef } ;
1589
- use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , TaggedHash , self } ;
1616
+ use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , TaggedHash , TlvStream , self } ;
1590
1617
use crate :: offers:: nonce:: Nonce ;
1591
1618
use crate :: offers:: offer:: { Amount , ExperimentalOfferTlvStreamRef , OfferTlvStreamRef , Quantity } ;
1592
1619
use crate :: prelude:: * ;
@@ -1762,7 +1789,9 @@ mod tests {
1762
1789
ExperimentalInvoiceRequestTlvStreamRef {
1763
1790
experimental_bar: None ,
1764
1791
} ,
1765
- ExperimentalInvoiceTlvStreamRef { } ,
1792
+ ExperimentalInvoiceTlvStreamRef {
1793
+ experimental_baz: None ,
1794
+ } ,
1766
1795
) ,
1767
1796
) ;
1768
1797
@@ -1862,7 +1891,9 @@ mod tests {
1862
1891
ExperimentalInvoiceRequestTlvStreamRef {
1863
1892
experimental_bar: None ,
1864
1893
} ,
1865
- ExperimentalInvoiceTlvStreamRef { } ,
1894
+ ExperimentalInvoiceTlvStreamRef {
1895
+ experimental_baz: None ,
1896
+ } ,
1866
1897
) ,
1867
1898
) ;
1868
1899
@@ -2713,6 +2744,97 @@ mod tests {
2713
2744
}
2714
2745
}
2715
2746
2747
+ #[ test]
2748
+ fn parses_invoice_with_experimental_tlv_records ( ) {
2749
+ let secp_ctx = Secp256k1 :: new ( ) ;
2750
+ let keys = Keypair :: from_secret_key ( & secp_ctx, & SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ) ;
2751
+ let invoice = OfferBuilder :: new ( keys. public_key ( ) )
2752
+ . amount_msats ( 1000 )
2753
+ . build ( ) . unwrap ( )
2754
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2755
+ . build ( ) . unwrap ( )
2756
+ . sign ( payer_sign) . unwrap ( )
2757
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2758
+ . experimental_baz ( 42 )
2759
+ . build ( ) . unwrap ( )
2760
+ . sign ( |message : & UnsignedBolt12Invoice |
2761
+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2762
+ )
2763
+ . unwrap ( ) ;
2764
+
2765
+ let mut buffer = Vec :: new ( ) ;
2766
+ invoice. write ( & mut buffer) . unwrap ( ) ;
2767
+
2768
+ assert ! ( Bolt12Invoice :: try_from( buffer) . is_ok( ) ) ;
2769
+
2770
+ const UNKNOWN_ODD_TYPE : u64 = EXPERIMENTAL_INVOICE_TYPES . start + 1 ;
2771
+ assert ! ( UNKNOWN_ODD_TYPE % 2 == 1 ) ;
2772
+
2773
+ let mut unsigned_invoice = OfferBuilder :: new ( keys. public_key ( ) )
2774
+ . amount_msats ( 1000 )
2775
+ . build ( ) . unwrap ( )
2776
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2777
+ . build ( ) . unwrap ( )
2778
+ . sign ( payer_sign) . unwrap ( )
2779
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2780
+ . build ( ) . unwrap ( ) ;
2781
+
2782
+ BigSize ( UNKNOWN_ODD_TYPE ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2783
+ BigSize ( 32 ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2784
+ [ 42u8 ; 32 ] . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2785
+
2786
+ let tlv_stream = TlvStream :: new ( & unsigned_invoice. bytes )
2787
+ . chain ( TlvStream :: new ( & unsigned_invoice. experimental_bytes ) ) ;
2788
+ unsigned_invoice. tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
2789
+
2790
+ let invoice = unsigned_invoice
2791
+ . sign ( |message : & UnsignedBolt12Invoice |
2792
+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2793
+ )
2794
+ . unwrap ( ) ;
2795
+
2796
+ let mut encoded_invoice = Vec :: new ( ) ;
2797
+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2798
+
2799
+ if let Err ( e) = Bolt12Invoice :: try_from ( encoded_invoice) {
2800
+ panic ! ( "error parsing invoice: {:?}" , e) ;
2801
+ }
2802
+
2803
+ const UNKNOWN_EVEN_TYPE : u64 = EXPERIMENTAL_INVOICE_TYPES . start ;
2804
+ assert ! ( UNKNOWN_EVEN_TYPE % 2 == 0 ) ;
2805
+
2806
+ let mut unsigned_invoice = OfferBuilder :: new ( keys. public_key ( ) )
2807
+ . amount_msats ( 1000 )
2808
+ . build ( ) . unwrap ( )
2809
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2810
+ . build ( ) . unwrap ( )
2811
+ . sign ( payer_sign) . unwrap ( )
2812
+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2813
+ . build ( ) . unwrap ( ) ;
2814
+
2815
+ BigSize ( UNKNOWN_EVEN_TYPE ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2816
+ BigSize ( 32 ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2817
+ [ 42u8 ; 32 ] . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2818
+
2819
+ let tlv_stream = TlvStream :: new ( & unsigned_invoice. bytes )
2820
+ . chain ( TlvStream :: new ( & unsigned_invoice. experimental_bytes ) ) ;
2821
+ unsigned_invoice. tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
2822
+
2823
+ let invoice = unsigned_invoice
2824
+ . sign ( |message : & UnsignedBolt12Invoice |
2825
+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2826
+ )
2827
+ . unwrap ( ) ;
2828
+
2829
+ let mut encoded_invoice = Vec :: new ( ) ;
2830
+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2831
+
2832
+ match Bolt12Invoice :: try_from ( encoded_invoice) {
2833
+ Ok ( _) => panic ! ( "expected error" ) ,
2834
+ Err ( e) => assert_eq ! ( e, Bolt12ParseError :: Decode ( DecodeError :: UnknownRequiredFeature ) ) ,
2835
+ }
2836
+ }
2837
+
2716
2838
#[ test]
2717
2839
fn fails_parsing_invoice_with_out_of_range_tlv_records ( ) {
2718
2840
let invoice = OfferBuilder :: new ( recipient_pubkey ( ) )
0 commit comments