@@ -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
@@ -634,6 +636,8 @@ struct InvoiceFields {
634636 fallbacks : Option < Vec < FallbackAddress > > ,
635637 features : Bolt12InvoiceFeatures ,
636638 signing_pubkey : PublicKey ,
639+ #[ cfg( test) ]
640+ experimental_baz : Option < u64 > ,
637641}
638642
639643macro_rules! invoice_accessors { ( $self: ident, $contents: expr) => {
@@ -1217,7 +1221,10 @@ impl InvoiceFields {
12171221 node_id : Some ( & self . signing_pubkey ) ,
12181222 message_paths : None ,
12191223 } ,
1220- ExperimentalInvoiceTlvStreamRef { } ,
1224+ ExperimentalInvoiceTlvStreamRef {
1225+ #[ cfg( test) ]
1226+ experimental_baz : self . experimental_baz ,
1227+ } ,
12211228 )
12221229 }
12231230}
@@ -1294,12 +1301,20 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
12941301} ) ;
12951302
12961303/// 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 ..;
12981305
1306+ #[ cfg( not( test) ) ]
12991307tlv_stream ! (
13001308 ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , { }
13011309) ;
13021310
1311+ #[ cfg( test) ]
1312+ tlv_stream ! (
1313+ ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , {
1314+ ( 3_999_999_999 , experimental_baz: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
1315+ }
1316+ ) ;
1317+
13031318pub ( super ) type BlindedPathIter < ' a > = core:: iter:: Map <
13041319 core:: slice:: Iter < ' a , BlindedPaymentPath > ,
13051320 for <' r > fn ( & ' r BlindedPaymentPath ) -> & ' r BlindedPath ,
@@ -1434,7 +1449,10 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14341449 } ,
14351450 experimental_offer_tlv_stream,
14361451 experimental_invoice_request_tlv_stream,
1437- ExperimentalInvoiceTlvStream { } ,
1452+ ExperimentalInvoiceTlvStream {
1453+ #[ cfg( test) ]
1454+ experimental_baz,
1455+ } ,
14381456 ) = tlv_stream;
14391457
14401458 if message_paths. is_some ( ) { return Err ( Bolt12SemanticError :: UnexpectedPaths ) }
@@ -1461,6 +1479,8 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14611479 let fields = InvoiceFields {
14621480 payment_paths, created_at, relative_expiry, payment_hash, amount_msats, fallbacks,
14631481 features, signing_pubkey,
1482+ #[ cfg( test) ]
1483+ experimental_baz,
14641484 } ;
14651485
14661486 check_invoice_signing_pubkey ( & fields. signing_pubkey , & offer_tlv_stream) ?;
@@ -1531,7 +1551,7 @@ pub(super) fn check_invoice_signing_pubkey(
15311551
15321552#[ cfg( test) ]
15331553mod 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 } ;
15351555
15361556 use bitcoin:: { CompressedPublicKey , WitnessProgram , WitnessVersion } ;
15371557 use bitcoin:: constants:: ChainHash ;
@@ -1551,7 +1571,7 @@ mod tests {
15511571 use crate :: ln:: inbound_payment:: ExpandedKey ;
15521572 use crate :: ln:: msgs:: DecodeError ;
15531573 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 } ;
15551575 use crate :: offers:: nonce:: Nonce ;
15561576 use crate :: offers:: offer:: { Amount , ExperimentalOfferTlvStreamRef , OfferTlvStreamRef , Quantity } ;
15571577 use crate :: prelude:: * ;
@@ -1727,7 +1747,9 @@ mod tests {
17271747 ExperimentalInvoiceRequestTlvStreamRef {
17281748 experimental_bar: None ,
17291749 } ,
1730- ExperimentalInvoiceTlvStreamRef { } ,
1750+ ExperimentalInvoiceTlvStreamRef {
1751+ experimental_baz: None ,
1752+ } ,
17311753 ) ,
17321754 ) ;
17331755
@@ -1827,7 +1849,9 @@ mod tests {
18271849 ExperimentalInvoiceRequestTlvStreamRef {
18281850 experimental_bar: None ,
18291851 } ,
1830- ExperimentalInvoiceTlvStreamRef { } ,
1852+ ExperimentalInvoiceTlvStreamRef {
1853+ experimental_baz: None ,
1854+ } ,
18311855 ) ,
18321856 ) ;
18331857
@@ -2674,6 +2698,97 @@ mod tests {
26742698 }
26752699 }
26762700
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+
26772792 #[ test]
26782793 fn fails_parsing_invoice_with_out_of_range_tlv_records ( ) {
26792794 let invoice = OfferBuilder :: new ( recipient_pubkey ( ) )
0 commit comments