@@ -363,6 +363,8 @@ macro_rules! invoice_builder_methods { (
363363 InvoiceFields {
364364 payment_paths, created_at, relative_expiry: None , payment_hash, amount_msats,
365365 fallbacks: None , features: Bolt12InvoiceFeatures :: empty( ) , signing_pubkey,
366+ #[ cfg( test) ]
367+ experimental_baz: None ,
366368 }
367369 }
368370
@@ -633,6 +635,8 @@ struct InvoiceFields {
633635 fallbacks : Option < Vec < FallbackAddress > > ,
634636 features : Bolt12InvoiceFeatures ,
635637 signing_pubkey : PublicKey ,
638+ #[ cfg( test) ]
639+ experimental_baz : Option < u64 > ,
636640}
637641
638642macro_rules! invoice_accessors { ( $self: ident, $contents: expr) => {
@@ -1216,7 +1220,10 @@ impl InvoiceFields {
12161220 node_id : Some ( & self . signing_pubkey ) ,
12171221 message_paths : None ,
12181222 } ,
1219- ExperimentalInvoiceTlvStreamRef { } ,
1223+ ExperimentalInvoiceTlvStreamRef {
1224+ #[ cfg( test) ]
1225+ experimental_baz : self . experimental_baz ,
1226+ } ,
12201227 )
12211228 }
12221229}
@@ -1305,12 +1312,20 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
13051312} ) ;
13061313
13071314/// 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 ..;
13091316
1317+ #[ cfg( not( test) ) ]
13101318tlv_stream ! (
13111319 ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , { }
13121320) ;
13131321
1322+ #[ cfg( test) ]
1323+ tlv_stream ! (
1324+ ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , {
1325+ ( 3_999_999_999 , experimental_baz: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
1326+ }
1327+ ) ;
1328+
13141329pub ( super ) type BlindedPathIter < ' a > = core:: iter:: Map <
13151330 core:: slice:: Iter < ' a , BlindedPaymentPath > ,
13161331 for <' r > fn ( & ' r BlindedPaymentPath ) -> & ' r BlindedPath ,
@@ -1445,7 +1460,10 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14451460 } ,
14461461 experimental_offer_tlv_stream,
14471462 experimental_invoice_request_tlv_stream,
1448- ExperimentalInvoiceTlvStream { } ,
1463+ ExperimentalInvoiceTlvStream {
1464+ #[ cfg( test) ]
1465+ experimental_baz,
1466+ } ,
14491467 ) = tlv_stream;
14501468
14511469 if message_paths. is_some ( ) { return Err ( Bolt12SemanticError :: UnexpectedPaths ) }
@@ -1472,6 +1490,8 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14721490 let fields = InvoiceFields {
14731491 payment_paths, created_at, relative_expiry, payment_hash, amount_msats, fallbacks,
14741492 features, signing_pubkey,
1493+ #[ cfg( test) ]
1494+ experimental_baz,
14751495 } ;
14761496
14771497 check_invoice_signing_pubkey ( & fields. signing_pubkey , & offer_tlv_stream) ?;
@@ -1542,7 +1562,7 @@ pub(super) fn check_invoice_signing_pubkey(
15421562
15431563#[ cfg( test) ]
15441564mod 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 } ;
15461566
15471567 use bitcoin:: { CompressedPublicKey , WitnessProgram , WitnessVersion } ;
15481568 use bitcoin:: constants:: ChainHash ;
@@ -1562,7 +1582,7 @@ mod tests {
15621582 use crate :: ln:: inbound_payment:: ExpandedKey ;
15631583 use crate :: ln:: msgs:: DecodeError ;
15641584 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 } ;
15661586 use crate :: offers:: nonce:: Nonce ;
15671587 use crate :: offers:: offer:: { Amount , ExperimentalOfferTlvStreamRef , OfferTlvStreamRef , Quantity } ;
15681588 use crate :: prelude:: * ;
@@ -1738,7 +1758,9 @@ mod tests {
17381758 ExperimentalInvoiceRequestTlvStreamRef {
17391759 experimental_bar: None ,
17401760 } ,
1741- ExperimentalInvoiceTlvStreamRef { } ,
1761+ ExperimentalInvoiceTlvStreamRef {
1762+ experimental_baz: None ,
1763+ } ,
17421764 ) ,
17431765 ) ;
17441766
@@ -1838,7 +1860,9 @@ mod tests {
18381860 ExperimentalInvoiceRequestTlvStreamRef {
18391861 experimental_bar: None ,
18401862 } ,
1841- ExperimentalInvoiceTlvStreamRef { } ,
1863+ ExperimentalInvoiceTlvStreamRef {
1864+ experimental_baz: None ,
1865+ } ,
18421866 ) ,
18431867 ) ;
18441868
@@ -2685,6 +2709,97 @@ mod tests {
26852709 }
26862710 }
26872711
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+
26882803 #[ test]
26892804 fn fails_parsing_invoice_with_out_of_range_tlv_records ( ) {
26902805 let invoice = OfferBuilder :: new ( recipient_pubkey ( ) )
0 commit comments