@@ -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
@@ -657,6 +659,8 @@ struct InvoiceFields {
657659 fallbacks : Option < Vec < FallbackAddress > > ,
658660 features : Bolt12InvoiceFeatures ,
659661 signing_pubkey : PublicKey ,
662+ #[ cfg( test) ]
663+ experimental_baz : Option < u64 > ,
660664}
661665
662666macro_rules! invoice_accessors { ( $self: ident, $contents: expr) => {
@@ -1247,7 +1251,10 @@ impl InvoiceFields {
12471251 node_id : Some ( & self . signing_pubkey ) ,
12481252 message_paths : None ,
12491253 } ,
1250- ExperimentalInvoiceTlvStreamRef { } ,
1254+ ExperimentalInvoiceTlvStreamRef {
1255+ #[ cfg( test) ]
1256+ experimental_baz : self . experimental_baz ,
1257+ } ,
12511258 )
12521259 }
12531260}
@@ -1324,12 +1331,20 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
13241331} ) ;
13251332
13261333/// Valid type range for experimental invoice TLV records.
1327- const EXPERIMENTAL_INVOICE_TYPES : core:: ops:: RangeFrom < u64 > = 3_000_000_000 ..;
1334+ pub ( super ) const EXPERIMENTAL_INVOICE_TYPES : core:: ops:: RangeFrom < u64 > = 3_000_000_000 ..;
13281335
1336+ #[ cfg( not( test) ) ]
13291337tlv_stream ! (
13301338 ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , { }
13311339) ;
13321340
1341+ #[ cfg( test) ]
1342+ tlv_stream ! (
1343+ ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , {
1344+ ( 3_999_999_999 , experimental_baz: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
1345+ }
1346+ ) ;
1347+
13331348pub ( super ) type BlindedPathIter < ' a > = core:: iter:: Map <
13341349 core:: slice:: Iter < ' a , BlindedPaymentPath > ,
13351350 for <' r > fn ( & ' r BlindedPaymentPath ) -> & ' r BlindedPath ,
@@ -1464,7 +1479,10 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14641479 } ,
14651480 experimental_offer_tlv_stream,
14661481 experimental_invoice_request_tlv_stream,
1467- ExperimentalInvoiceTlvStream { } ,
1482+ ExperimentalInvoiceTlvStream {
1483+ #[ cfg( test) ]
1484+ experimental_baz,
1485+ } ,
14681486 ) = tlv_stream;
14691487
14701488 if message_paths. is_some ( ) { return Err ( Bolt12SemanticError :: UnexpectedPaths ) }
@@ -1491,6 +1509,8 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14911509 let fields = InvoiceFields {
14921510 payment_paths, created_at, relative_expiry, payment_hash, amount_msats, fallbacks,
14931511 features, signing_pubkey,
1512+ #[ cfg( test) ]
1513+ experimental_baz,
14941514 } ;
14951515
14961516 check_invoice_signing_pubkey ( & fields. signing_pubkey , & offer_tlv_stream) ?;
@@ -1561,7 +1581,7 @@ pub(super) fn check_invoice_signing_pubkey(
15611581
15621582#[ cfg( test) ]
15631583mod tests {
1564- use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , ExperimentalInvoiceTlvStreamRef , FallbackAddress , FullInvoiceTlvStreamRef , INVOICE_TYPES , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
1584+ use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , EXPERIMENTAL_INVOICE_TYPES , ExperimentalInvoiceTlvStreamRef , FallbackAddress , FullInvoiceTlvStreamRef , INVOICE_TYPES , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
15651585
15661586 use bitcoin:: { CompressedPublicKey , WitnessProgram , WitnessVersion } ;
15671587 use bitcoin:: constants:: ChainHash ;
@@ -1581,7 +1601,7 @@ mod tests {
15811601 use crate :: ln:: inbound_payment:: ExpandedKey ;
15821602 use crate :: ln:: msgs:: DecodeError ;
15831603 use crate :: offers:: invoice_request:: { ExperimentalInvoiceRequestTlvStreamRef , InvoiceRequestTlvStreamRef } ;
1584- use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , TaggedHash , self } ;
1604+ use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , TaggedHash , TlvStream , self } ;
15851605 use crate :: offers:: nonce:: Nonce ;
15861606 use crate :: offers:: offer:: { Amount , ExperimentalOfferTlvStreamRef , OfferTlvStreamRef , Quantity } ;
15871607 use crate :: prelude:: * ;
@@ -1757,7 +1777,9 @@ mod tests {
17571777 ExperimentalInvoiceRequestTlvStreamRef {
17581778 experimental_bar: None ,
17591779 } ,
1760- ExperimentalInvoiceTlvStreamRef { } ,
1780+ ExperimentalInvoiceTlvStreamRef {
1781+ experimental_baz: None ,
1782+ } ,
17611783 ) ,
17621784 ) ;
17631785
@@ -1857,7 +1879,9 @@ mod tests {
18571879 ExperimentalInvoiceRequestTlvStreamRef {
18581880 experimental_bar: None ,
18591881 } ,
1860- ExperimentalInvoiceTlvStreamRef { } ,
1882+ ExperimentalInvoiceTlvStreamRef {
1883+ experimental_baz: None ,
1884+ } ,
18611885 ) ,
18621886 ) ;
18631887
@@ -2705,6 +2729,123 @@ mod tests {
27052729 }
27062730 }
27072731
2732+ #[ test]
2733+ fn parses_invoice_with_experimental_tlv_records ( ) {
2734+ let secp_ctx = Secp256k1 :: new ( ) ;
2735+ let keys = Keypair :: from_secret_key ( & secp_ctx, & SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ) ;
2736+ let invoice = OfferBuilder :: new ( keys. public_key ( ) )
2737+ . amount_msats ( 1000 )
2738+ . build ( ) . unwrap ( )
2739+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2740+ . build ( ) . unwrap ( )
2741+ . sign ( payer_sign) . unwrap ( )
2742+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2743+ . experimental_baz ( 42 )
2744+ . build ( ) . unwrap ( )
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+ assert ! ( Bolt12Invoice :: try_from( encoded_invoice) . is_ok( ) ) ;
2754+
2755+ const UNKNOWN_ODD_TYPE : u64 = EXPERIMENTAL_INVOICE_TYPES . start + 1 ;
2756+ assert ! ( UNKNOWN_ODD_TYPE % 2 == 1 ) ;
2757+
2758+ let mut unsigned_invoice = OfferBuilder :: new ( keys. public_key ( ) )
2759+ . amount_msats ( 1000 )
2760+ . build ( ) . unwrap ( )
2761+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2762+ . build ( ) . unwrap ( )
2763+ . sign ( payer_sign) . unwrap ( )
2764+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2765+ . build ( ) . unwrap ( ) ;
2766+
2767+ BigSize ( UNKNOWN_ODD_TYPE ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2768+ BigSize ( 32 ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2769+ [ 42u8 ; 32 ] . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2770+
2771+ let tlv_stream = TlvStream :: new ( & unsigned_invoice. bytes )
2772+ . chain ( TlvStream :: new ( & unsigned_invoice. experimental_bytes ) ) ;
2773+ unsigned_invoice. tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
2774+
2775+ let invoice = unsigned_invoice
2776+ . sign ( |message : & UnsignedBolt12Invoice |
2777+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2778+ )
2779+ . unwrap ( ) ;
2780+
2781+ let mut encoded_invoice = Vec :: new ( ) ;
2782+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2783+
2784+ match Bolt12Invoice :: try_from ( encoded_invoice. clone ( ) ) {
2785+ Ok ( invoice) => assert_eq ! ( invoice. bytes, encoded_invoice) ,
2786+ Err ( e) => panic ! ( "error parsing invoice: {:?}" , e) ,
2787+ }
2788+
2789+ const UNKNOWN_EVEN_TYPE : u64 = EXPERIMENTAL_INVOICE_TYPES . start ;
2790+ assert ! ( UNKNOWN_EVEN_TYPE % 2 == 0 ) ;
2791+
2792+ let mut unsigned_invoice = OfferBuilder :: new ( keys. public_key ( ) )
2793+ . amount_msats ( 1000 )
2794+ . build ( ) . unwrap ( )
2795+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2796+ . build ( ) . unwrap ( )
2797+ . sign ( payer_sign) . unwrap ( )
2798+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2799+ . build ( ) . unwrap ( ) ;
2800+
2801+ BigSize ( UNKNOWN_EVEN_TYPE ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2802+ BigSize ( 32 ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2803+ [ 42u8 ; 32 ] . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2804+
2805+ let tlv_stream = TlvStream :: new ( & unsigned_invoice. bytes )
2806+ . chain ( TlvStream :: new ( & unsigned_invoice. experimental_bytes ) ) ;
2807+ unsigned_invoice. tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
2808+
2809+ let invoice = unsigned_invoice
2810+ . sign ( |message : & UnsignedBolt12Invoice |
2811+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2812+ )
2813+ . unwrap ( ) ;
2814+
2815+ let mut encoded_invoice = Vec :: new ( ) ;
2816+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2817+
2818+ match Bolt12Invoice :: try_from ( encoded_invoice) {
2819+ Ok ( _) => panic ! ( "expected error" ) ,
2820+ Err ( e) => assert_eq ! ( e, Bolt12ParseError :: Decode ( DecodeError :: UnknownRequiredFeature ) ) ,
2821+ }
2822+
2823+ let invoice = OfferBuilder :: new ( keys. public_key ( ) )
2824+ . amount_msats ( 1000 )
2825+ . build ( ) . unwrap ( )
2826+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2827+ . build ( ) . unwrap ( )
2828+ . sign ( payer_sign) . unwrap ( )
2829+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2830+ . build ( ) . unwrap ( )
2831+ . sign ( |message : & UnsignedBolt12Invoice |
2832+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2833+ )
2834+ . unwrap ( ) ;
2835+
2836+ let mut encoded_invoice = Vec :: new ( ) ;
2837+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2838+
2839+ BigSize ( UNKNOWN_ODD_TYPE ) . write ( & mut encoded_invoice) . unwrap ( ) ;
2840+ BigSize ( 32 ) . write ( & mut encoded_invoice) . unwrap ( ) ;
2841+ [ 42u8 ; 32 ] . write ( & mut encoded_invoice) . unwrap ( ) ;
2842+
2843+ match Bolt12Invoice :: try_from ( encoded_invoice) {
2844+ Ok ( _) => panic ! ( "expected error" ) ,
2845+ Err ( e) => assert_eq ! ( e, Bolt12ParseError :: InvalidSignature ( secp256k1:: Error :: IncorrectSignature ) ) ,
2846+ }
2847+ }
2848+
27082849 #[ test]
27092850 fn fails_parsing_invoice_with_out_of_range_tlv_records ( ) {
27102851 let invoice = OfferBuilder :: new ( recipient_pubkey ( ) )
0 commit comments