@@ -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
@@ -666,6 +668,8 @@ struct InvoiceFields {
666668 fallbacks : Option < Vec < FallbackAddress > > ,
667669 features : Bolt12InvoiceFeatures ,
668670 signing_pubkey : PublicKey ,
671+ #[ cfg( test) ]
672+ experimental_baz : Option < u64 > ,
669673}
670674
671675macro_rules! invoice_accessors { ( $self: ident, $contents: expr) => {
@@ -1256,7 +1260,10 @@ impl InvoiceFields {
12561260 node_id : Some ( & self . signing_pubkey ) ,
12571261 message_paths : None ,
12581262 } ,
1259- ExperimentalInvoiceTlvStreamRef { } ,
1263+ ExperimentalInvoiceTlvStreamRef {
1264+ #[ cfg( test) ]
1265+ experimental_baz : self . experimental_baz ,
1266+ } ,
12601267 )
12611268 }
12621269}
@@ -1333,12 +1340,20 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
13331340} ) ;
13341341
13351342/// Valid type range for experimental invoice TLV records.
1336- const EXPERIMENTAL_INVOICE_TYPES : core:: ops:: RangeFrom < u64 > = 3_000_000_000 ..;
1343+ pub ( super ) const EXPERIMENTAL_INVOICE_TYPES : core:: ops:: RangeFrom < u64 > = 3_000_000_000 ..;
13371344
1345+ #[ cfg( not( test) ) ]
13381346tlv_stream ! (
13391347 ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , { }
13401348) ;
13411349
1350+ #[ cfg( test) ]
1351+ tlv_stream ! (
1352+ ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , {
1353+ ( 3_999_999_999 , experimental_baz: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
1354+ }
1355+ ) ;
1356+
13421357pub ( super ) type BlindedPathIter < ' a > = core:: iter:: Map <
13431358 core:: slice:: Iter < ' a , BlindedPaymentPath > ,
13441359 for <' r > fn ( & ' r BlindedPaymentPath ) -> & ' r BlindedPath ,
@@ -1473,7 +1488,10 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14731488 } ,
14741489 experimental_offer_tlv_stream,
14751490 experimental_invoice_request_tlv_stream,
1476- ExperimentalInvoiceTlvStream { } ,
1491+ ExperimentalInvoiceTlvStream {
1492+ #[ cfg( test) ]
1493+ experimental_baz,
1494+ } ,
14771495 ) = tlv_stream;
14781496
14791497 if message_paths. is_some ( ) { return Err ( Bolt12SemanticError :: UnexpectedPaths ) }
@@ -1500,6 +1518,8 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
15001518 let fields = InvoiceFields {
15011519 payment_paths, created_at, relative_expiry, payment_hash, amount_msats, fallbacks,
15021520 features, signing_pubkey,
1521+ #[ cfg( test) ]
1522+ experimental_baz,
15031523 } ;
15041524
15051525 check_invoice_signing_pubkey ( & fields. signing_pubkey , & offer_tlv_stream) ?;
@@ -1570,7 +1590,7 @@ pub(super) fn check_invoice_signing_pubkey(
15701590
15711591#[ cfg( test) ]
15721592mod tests {
1573- use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , ExperimentalInvoiceTlvStreamRef , FallbackAddress , FullInvoiceTlvStreamRef , INVOICE_TYPES , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
1593+ use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , EXPERIMENTAL_INVOICE_TYPES , ExperimentalInvoiceTlvStreamRef , FallbackAddress , FullInvoiceTlvStreamRef , INVOICE_TYPES , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
15741594
15751595 use bitcoin:: { CompressedPublicKey , WitnessProgram , WitnessVersion } ;
15761596 use bitcoin:: constants:: ChainHash ;
@@ -1590,7 +1610,7 @@ mod tests {
15901610 use crate :: ln:: inbound_payment:: ExpandedKey ;
15911611 use crate :: ln:: msgs:: DecodeError ;
15921612 use crate :: offers:: invoice_request:: { ExperimentalInvoiceRequestTlvStreamRef , InvoiceRequestTlvStreamRef } ;
1593- use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , TaggedHash , self } ;
1613+ use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , TaggedHash , TlvStream , self } ;
15941614 use crate :: offers:: nonce:: Nonce ;
15951615 use crate :: offers:: offer:: { Amount , ExperimentalOfferTlvStreamRef , OfferTlvStreamRef , Quantity } ;
15961616 use crate :: prelude:: * ;
@@ -1766,7 +1786,9 @@ mod tests {
17661786 ExperimentalInvoiceRequestTlvStreamRef {
17671787 experimental_bar: None ,
17681788 } ,
1769- ExperimentalInvoiceTlvStreamRef { } ,
1789+ ExperimentalInvoiceTlvStreamRef {
1790+ experimental_baz: None ,
1791+ } ,
17701792 ) ,
17711793 ) ;
17721794
@@ -1866,7 +1888,9 @@ mod tests {
18661888 ExperimentalInvoiceRequestTlvStreamRef {
18671889 experimental_bar: None ,
18681890 } ,
1869- ExperimentalInvoiceTlvStreamRef { } ,
1891+ ExperimentalInvoiceTlvStreamRef {
1892+ experimental_baz: None ,
1893+ } ,
18701894 ) ,
18711895 ) ;
18721896
@@ -2724,6 +2748,135 @@ mod tests {
27242748 }
27252749 }
27262750
2751+ #[ test]
2752+ fn parses_invoice_with_experimental_tlv_records ( ) {
2753+ let secp_ctx = Secp256k1 :: new ( ) ;
2754+ let keys = Keypair :: from_secret_key ( & secp_ctx, & SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ) ;
2755+ let invoice = OfferBuilder :: new ( keys. public_key ( ) )
2756+ . amount_msats ( 1000 )
2757+ . build ( ) . unwrap ( )
2758+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2759+ . build ( ) . unwrap ( )
2760+ . sign ( payer_sign) . unwrap ( )
2761+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2762+ . experimental_baz ( 42 )
2763+ . build ( ) . unwrap ( )
2764+ . sign ( |message : & UnsignedBolt12Invoice |
2765+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2766+ )
2767+ . unwrap ( ) ;
2768+
2769+ let mut encoded_invoice = Vec :: new ( ) ;
2770+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2771+
2772+ assert ! ( Bolt12Invoice :: try_from( encoded_invoice) . is_ok( ) ) ;
2773+
2774+ const UNKNOWN_ODD_TYPE : u64 = EXPERIMENTAL_INVOICE_TYPES . start + 1 ;
2775+ assert ! ( UNKNOWN_ODD_TYPE % 2 == 1 ) ;
2776+
2777+ let mut unsigned_invoice = OfferBuilder :: new ( keys. public_key ( ) )
2778+ . amount_msats ( 1000 )
2779+ . build ( ) . unwrap ( )
2780+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2781+ . build ( ) . unwrap ( )
2782+ . sign ( payer_sign) . unwrap ( )
2783+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2784+ . build ( ) . unwrap ( ) ;
2785+
2786+ let mut unknown_bytes = Vec :: new ( ) ;
2787+ BigSize ( UNKNOWN_ODD_TYPE ) . write ( & mut unknown_bytes) . unwrap ( ) ;
2788+ BigSize ( 32 ) . write ( & mut unknown_bytes) . unwrap ( ) ;
2789+ [ 42u8 ; 32 ] . write ( & mut unknown_bytes) . unwrap ( ) ;
2790+
2791+ unsigned_invoice. bytes . reserve_exact (
2792+ unsigned_invoice. bytes . capacity ( ) - unsigned_invoice. bytes . len ( ) + unknown_bytes. len ( ) ,
2793+ ) ;
2794+ unsigned_invoice. experimental_bytes . extend_from_slice ( & unknown_bytes) ;
2795+
2796+ let tlv_stream = TlvStream :: new ( & unsigned_invoice. bytes )
2797+ . chain ( TlvStream :: new ( & unsigned_invoice. experimental_bytes ) ) ;
2798+ unsigned_invoice. tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
2799+
2800+ let invoice = unsigned_invoice
2801+ . sign ( |message : & UnsignedBolt12Invoice |
2802+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2803+ )
2804+ . unwrap ( ) ;
2805+
2806+ let mut encoded_invoice = Vec :: new ( ) ;
2807+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2808+
2809+ match Bolt12Invoice :: try_from ( encoded_invoice. clone ( ) ) {
2810+ Ok ( invoice) => assert_eq ! ( invoice. bytes, encoded_invoice) ,
2811+ Err ( e) => panic ! ( "error parsing invoice: {:?}" , e) ,
2812+ }
2813+
2814+ const UNKNOWN_EVEN_TYPE : u64 = EXPERIMENTAL_INVOICE_TYPES . start ;
2815+ assert ! ( UNKNOWN_EVEN_TYPE % 2 == 0 ) ;
2816+
2817+ let mut unsigned_invoice = OfferBuilder :: new ( keys. public_key ( ) )
2818+ . amount_msats ( 1000 )
2819+ . build ( ) . unwrap ( )
2820+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2821+ . build ( ) . unwrap ( )
2822+ . sign ( payer_sign) . unwrap ( )
2823+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2824+ . build ( ) . unwrap ( ) ;
2825+
2826+ let mut unknown_bytes = Vec :: new ( ) ;
2827+ BigSize ( UNKNOWN_EVEN_TYPE ) . write ( & mut unknown_bytes) . unwrap ( ) ;
2828+ BigSize ( 32 ) . write ( & mut unknown_bytes) . unwrap ( ) ;
2829+ [ 42u8 ; 32 ] . write ( & mut unknown_bytes) . unwrap ( ) ;
2830+
2831+ unsigned_invoice. bytes . reserve_exact (
2832+ unsigned_invoice. bytes . capacity ( ) - unsigned_invoice. bytes . len ( ) + unknown_bytes. len ( ) ,
2833+ ) ;
2834+ unsigned_invoice. experimental_bytes . extend_from_slice ( & unknown_bytes) ;
2835+
2836+ let tlv_stream = TlvStream :: new ( & unsigned_invoice. bytes )
2837+ . chain ( TlvStream :: new ( & unsigned_invoice. experimental_bytes ) ) ;
2838+ unsigned_invoice. tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
2839+
2840+ let invoice = unsigned_invoice
2841+ . sign ( |message : & UnsignedBolt12Invoice |
2842+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2843+ )
2844+ . unwrap ( ) ;
2845+
2846+ let mut encoded_invoice = Vec :: new ( ) ;
2847+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2848+
2849+ match Bolt12Invoice :: try_from ( encoded_invoice) {
2850+ Ok ( _) => panic ! ( "expected error" ) ,
2851+ Err ( e) => assert_eq ! ( e, Bolt12ParseError :: Decode ( DecodeError :: UnknownRequiredFeature ) ) ,
2852+ }
2853+
2854+ let invoice = OfferBuilder :: new ( keys. public_key ( ) )
2855+ . amount_msats ( 1000 )
2856+ . build ( ) . unwrap ( )
2857+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2858+ . build ( ) . unwrap ( )
2859+ . sign ( payer_sign) . unwrap ( )
2860+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2861+ . build ( ) . unwrap ( )
2862+ . sign ( |message : & UnsignedBolt12Invoice |
2863+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2864+ )
2865+ . unwrap ( ) ;
2866+
2867+ let mut encoded_invoice = Vec :: new ( ) ;
2868+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2869+
2870+ BigSize ( UNKNOWN_ODD_TYPE ) . write ( & mut encoded_invoice) . unwrap ( ) ;
2871+ BigSize ( 32 ) . write ( & mut encoded_invoice) . unwrap ( ) ;
2872+ [ 42u8 ; 32 ] . write ( & mut encoded_invoice) . unwrap ( ) ;
2873+
2874+ match Bolt12Invoice :: try_from ( encoded_invoice) {
2875+ Ok ( _) => panic ! ( "expected error" ) ,
2876+ Err ( e) => assert_eq ! ( e, Bolt12ParseError :: InvalidSignature ( secp256k1:: Error :: IncorrectSignature ) ) ,
2877+ }
2878+ }
2879+
27272880 #[ test]
27282881 fn fails_parsing_invoice_with_out_of_range_tlv_records ( ) {
27292882 let invoice = OfferBuilder :: new ( recipient_pubkey ( ) )
0 commit comments