@@ -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
@@ -656,6 +658,8 @@ struct InvoiceFields {
656658 fallbacks : Option < Vec < FallbackAddress > > ,
657659 features : Bolt12InvoiceFeatures ,
658660 signing_pubkey : PublicKey ,
661+ #[ cfg( test) ]
662+ experimental_baz : Option < u64 > ,
659663}
660664
661665macro_rules! invoice_accessors { ( $self: ident, $contents: expr) => {
@@ -1239,7 +1243,10 @@ impl InvoiceFields {
12391243 node_id : Some ( & self . signing_pubkey ) ,
12401244 message_paths : None ,
12411245 } ,
1242- ExperimentalInvoiceTlvStreamRef { } ,
1246+ ExperimentalInvoiceTlvStreamRef {
1247+ #[ cfg( test) ]
1248+ experimental_baz : self . experimental_baz ,
1249+ } ,
12431250 )
12441251 }
12451252}
@@ -1316,12 +1323,20 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
13161323} ) ;
13171324
13181325/// Valid type range for experimental invoice TLV records.
1319- const EXPERIMENTAL_INVOICE_TYPES : core:: ops:: RangeFrom < u64 > = 3_000_000_000 ..;
1326+ pub ( super ) const EXPERIMENTAL_INVOICE_TYPES : core:: ops:: RangeFrom < u64 > = 3_000_000_000 ..;
13201327
1328+ #[ cfg( not( test) ) ]
13211329tlv_stream ! (
13221330 ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , { }
13231331) ;
13241332
1333+ #[ cfg( test) ]
1334+ tlv_stream ! (
1335+ ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , {
1336+ ( 3_999_999_999 , experimental_baz: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
1337+ }
1338+ ) ;
1339+
13251340pub ( super ) type BlindedPathIter < ' a > = core:: iter:: Map <
13261341 core:: slice:: Iter < ' a , BlindedPaymentPath > ,
13271342 for <' r > fn ( & ' r BlindedPaymentPath ) -> & ' r BlindedPath ,
@@ -1456,7 +1471,10 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14561471 } ,
14571472 experimental_offer_tlv_stream,
14581473 experimental_invoice_request_tlv_stream,
1459- ExperimentalInvoiceTlvStream { } ,
1474+ ExperimentalInvoiceTlvStream {
1475+ #[ cfg( test) ]
1476+ experimental_baz,
1477+ } ,
14601478 ) = tlv_stream;
14611479
14621480 if message_paths. is_some ( ) { return Err ( Bolt12SemanticError :: UnexpectedPaths ) }
@@ -1483,6 +1501,8 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14831501 let fields = InvoiceFields {
14841502 payment_paths, created_at, relative_expiry, payment_hash, amount_msats, fallbacks,
14851503 features, signing_pubkey,
1504+ #[ cfg( test) ]
1505+ experimental_baz,
14861506 } ;
14871507
14881508 check_invoice_signing_pubkey ( & fields. signing_pubkey , & offer_tlv_stream) ?;
@@ -1553,7 +1573,7 @@ pub(super) fn check_invoice_signing_pubkey(
15531573
15541574#[ cfg( test) ]
15551575mod tests {
1556- use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , ExperimentalInvoiceTlvStreamRef , FallbackAddress , FullInvoiceTlvStreamRef , INVOICE_TYPES , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
1576+ use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , EXPERIMENTAL_INVOICE_TYPES , ExperimentalInvoiceTlvStreamRef , FallbackAddress , FullInvoiceTlvStreamRef , INVOICE_TYPES , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
15571577
15581578 use bitcoin:: { CompressedPublicKey , WitnessProgram , WitnessVersion } ;
15591579 use bitcoin:: constants:: ChainHash ;
@@ -1573,7 +1593,7 @@ mod tests {
15731593 use crate :: ln:: inbound_payment:: ExpandedKey ;
15741594 use crate :: ln:: msgs:: DecodeError ;
15751595 use crate :: offers:: invoice_request:: { ExperimentalInvoiceRequestTlvStreamRef , InvoiceRequestTlvStreamRef } ;
1576- use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , TaggedHash , self } ;
1596+ use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , TaggedHash , TlvStream , self } ;
15771597 use crate :: offers:: nonce:: Nonce ;
15781598 use crate :: offers:: offer:: { Amount , ExperimentalOfferTlvStreamRef , OfferTlvStreamRef , Quantity } ;
15791599 use crate :: prelude:: * ;
@@ -1749,7 +1769,9 @@ mod tests {
17491769 ExperimentalInvoiceRequestTlvStreamRef {
17501770 experimental_bar: None ,
17511771 } ,
1752- ExperimentalInvoiceTlvStreamRef { } ,
1772+ ExperimentalInvoiceTlvStreamRef {
1773+ experimental_baz: None ,
1774+ } ,
17531775 ) ,
17541776 ) ;
17551777
@@ -1849,7 +1871,9 @@ mod tests {
18491871 ExperimentalInvoiceRequestTlvStreamRef {
18501872 experimental_bar: None ,
18511873 } ,
1852- ExperimentalInvoiceTlvStreamRef { } ,
1874+ ExperimentalInvoiceTlvStreamRef {
1875+ experimental_baz: None ,
1876+ } ,
18531877 ) ,
18541878 ) ;
18551879
@@ -2697,6 +2721,123 @@ mod tests {
26972721 }
26982722 }
26992723
2724+ #[ test]
2725+ fn parses_invoice_with_experimental_tlv_records ( ) {
2726+ let secp_ctx = Secp256k1 :: new ( ) ;
2727+ let keys = Keypair :: from_secret_key ( & secp_ctx, & SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ) ;
2728+ let invoice = OfferBuilder :: new ( keys. public_key ( ) )
2729+ . amount_msats ( 1000 )
2730+ . build ( ) . unwrap ( )
2731+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2732+ . build ( ) . unwrap ( )
2733+ . sign ( payer_sign) . unwrap ( )
2734+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2735+ . experimental_baz ( 42 )
2736+ . build ( ) . unwrap ( )
2737+ . sign ( |message : & UnsignedBolt12Invoice |
2738+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2739+ )
2740+ . unwrap ( ) ;
2741+
2742+ let mut encoded_invoice = Vec :: new ( ) ;
2743+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2744+
2745+ assert ! ( Bolt12Invoice :: try_from( encoded_invoice) . is_ok( ) ) ;
2746+
2747+ const UNKNOWN_ODD_TYPE : u64 = EXPERIMENTAL_INVOICE_TYPES . start + 1 ;
2748+ assert ! ( UNKNOWN_ODD_TYPE % 2 == 1 ) ;
2749+
2750+ let mut unsigned_invoice = OfferBuilder :: new ( keys. public_key ( ) )
2751+ . amount_msats ( 1000 )
2752+ . build ( ) . unwrap ( )
2753+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2754+ . build ( ) . unwrap ( )
2755+ . sign ( payer_sign) . unwrap ( )
2756+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2757+ . build ( ) . unwrap ( ) ;
2758+
2759+ BigSize ( UNKNOWN_ODD_TYPE ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2760+ BigSize ( 32 ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2761+ [ 42u8 ; 32 ] . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2762+
2763+ let tlv_stream = TlvStream :: new ( & unsigned_invoice. bytes )
2764+ . chain ( TlvStream :: new ( & unsigned_invoice. experimental_bytes ) ) ;
2765+ unsigned_invoice. tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
2766+
2767+ let invoice = unsigned_invoice
2768+ . sign ( |message : & UnsignedBolt12Invoice |
2769+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2770+ )
2771+ . unwrap ( ) ;
2772+
2773+ let mut encoded_invoice = Vec :: new ( ) ;
2774+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2775+
2776+ match Bolt12Invoice :: try_from ( encoded_invoice. clone ( ) ) {
2777+ Ok ( invoice) => assert_eq ! ( invoice. bytes, encoded_invoice) ,
2778+ Err ( e) => panic ! ( "error parsing invoice: {:?}" , e) ,
2779+ }
2780+
2781+ const UNKNOWN_EVEN_TYPE : u64 = EXPERIMENTAL_INVOICE_TYPES . start ;
2782+ assert ! ( UNKNOWN_EVEN_TYPE % 2 == 0 ) ;
2783+
2784+ let mut unsigned_invoice = OfferBuilder :: new ( keys. public_key ( ) )
2785+ . amount_msats ( 1000 )
2786+ . build ( ) . unwrap ( )
2787+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2788+ . build ( ) . unwrap ( )
2789+ . sign ( payer_sign) . unwrap ( )
2790+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2791+ . build ( ) . unwrap ( ) ;
2792+
2793+ BigSize ( UNKNOWN_EVEN_TYPE ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2794+ BigSize ( 32 ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2795+ [ 42u8 ; 32 ] . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2796+
2797+ let tlv_stream = TlvStream :: new ( & unsigned_invoice. bytes )
2798+ . chain ( TlvStream :: new ( & unsigned_invoice. experimental_bytes ) ) ;
2799+ unsigned_invoice. tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
2800+
2801+ let invoice = unsigned_invoice
2802+ . sign ( |message : & UnsignedBolt12Invoice |
2803+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2804+ )
2805+ . unwrap ( ) ;
2806+
2807+ let mut encoded_invoice = Vec :: new ( ) ;
2808+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2809+
2810+ match Bolt12Invoice :: try_from ( encoded_invoice) {
2811+ Ok ( _) => panic ! ( "expected error" ) ,
2812+ Err ( e) => assert_eq ! ( e, Bolt12ParseError :: Decode ( DecodeError :: UnknownRequiredFeature ) ) ,
2813+ }
2814+
2815+ let invoice = OfferBuilder :: new ( keys. public_key ( ) )
2816+ . amount_msats ( 1000 )
2817+ . build ( ) . unwrap ( )
2818+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2819+ . build ( ) . unwrap ( )
2820+ . sign ( payer_sign) . unwrap ( )
2821+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2822+ . build ( ) . unwrap ( )
2823+ . sign ( |message : & UnsignedBolt12Invoice |
2824+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2825+ )
2826+ . unwrap ( ) ;
2827+
2828+ let mut encoded_invoice = Vec :: new ( ) ;
2829+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2830+
2831+ BigSize ( UNKNOWN_ODD_TYPE ) . write ( & mut encoded_invoice) . unwrap ( ) ;
2832+ BigSize ( 32 ) . write ( & mut encoded_invoice) . unwrap ( ) ;
2833+ [ 42u8 ; 32 ] . write ( & mut encoded_invoice) . unwrap ( ) ;
2834+
2835+ match Bolt12Invoice :: try_from ( encoded_invoice) {
2836+ Ok ( _) => panic ! ( "expected error" ) ,
2837+ Err ( e) => assert_eq ! ( e, Bolt12ParseError :: InvalidSignature ( secp256k1:: Error :: IncorrectSignature ) ) ,
2838+ }
2839+ }
2840+
27002841 #[ test]
27012842 fn fails_parsing_invoice_with_out_of_range_tlv_records ( ) {
27022843 let invoice = OfferBuilder :: new ( recipient_pubkey ( ) )
0 commit comments