@@ -117,6 +117,8 @@ use crate::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures, InvoiceRequ
117117use crate :: ln:: inbound_payment:: { ExpandedKey , IV_LEN } ;
118118use crate :: ln:: msgs:: DecodeError ;
119119use crate :: offers:: invoice_macros:: { invoice_accessors_common, invoice_builder_methods_common} ;
120+ #[ cfg( test) ]
121+ use crate :: offers:: invoice_macros:: invoice_builder_test_methods;
120122use crate :: offers:: invoice_request:: { EXPERIMENTAL_INVOICE_REQUEST_TYPES , ExperimentalInvoiceRequestTlvStream , ExperimentalInvoiceRequestTlvStreamRef , INVOICE_REQUEST_PAYER_ID_TYPE , INVOICE_REQUEST_TYPES , IV_BYTES as INVOICE_REQUEST_IV_BYTES , InvoiceRequest , InvoiceRequestContents , InvoiceRequestTlvStream , InvoiceRequestTlvStreamRef } ;
121123use crate :: offers:: merkle:: { SignError , SignFn , SignatureTlvStream , SignatureTlvStreamRef , TaggedHash , TlvStream , self } ;
122124use crate :: offers:: nonce:: Nonce ;
@@ -359,6 +361,8 @@ macro_rules! invoice_builder_methods { (
359361 InvoiceFields {
360362 payment_paths, created_at, relative_expiry: None , payment_hash, amount_msats,
361363 fallbacks: None , features: Bolt12InvoiceFeatures :: empty( ) , signing_pubkey,
364+ #[ cfg( test) ]
365+ experimental_baz: None ,
362366 }
363367 }
364368
@@ -385,6 +389,9 @@ impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> {
385389impl < ' a , S : SigningPubkeyStrategy > InvoiceBuilder < ' a , S > {
386390 invoice_builder_methods ! ( self , Self , Self , self , S , mut ) ;
387391 invoice_builder_methods_common ! ( self , Self , self . invoice. fields_mut( ) , Self , self , S , Bolt12Invoice , mut ) ;
392+
393+ #[ cfg( test) ]
394+ invoice_builder_test_methods ! ( self , Self , self . invoice. fields_mut( ) , Self , self , mut ) ;
388395}
389396
390397#[ cfg( all( c_bindings, not( test) ) ) ]
@@ -399,6 +406,7 @@ impl<'a> InvoiceWithExplicitSigningPubkeyBuilder<'a> {
399406 invoice_explicit_signing_pubkey_builder_methods ! ( self , & mut Self ) ;
400407 invoice_builder_methods ! ( self , & mut Self , & mut Self , self , ExplicitSigningPubkey ) ;
401408 invoice_builder_methods_common ! ( self , & mut Self , self . invoice. fields_mut( ) , & mut Self , self , ExplicitSigningPubkey , Bolt12Invoice ) ;
409+ invoice_builder_test_methods ! ( self , & mut Self , self . invoice. fields_mut( ) , & mut Self , self ) ;
402410}
403411
404412#[ cfg( all( c_bindings, not( test) ) ) ]
@@ -413,6 +421,7 @@ impl<'a> InvoiceWithDerivedSigningPubkeyBuilder<'a> {
413421 invoice_derived_signing_pubkey_builder_methods ! ( self , & mut Self ) ;
414422 invoice_builder_methods ! ( self , & mut Self , & mut Self , self , DerivedSigningPubkey ) ;
415423 invoice_builder_methods_common ! ( self , & mut Self , self . invoice. fields_mut( ) , & mut Self , self , DerivedSigningPubkey , Bolt12Invoice ) ;
424+ invoice_builder_test_methods ! ( self , & mut Self , self . invoice. fields_mut( ) , & mut Self , self ) ;
416425}
417426
418427#[ cfg( c_bindings) ]
@@ -624,6 +633,8 @@ struct InvoiceFields {
624633 fallbacks : Option < Vec < FallbackAddress > > ,
625634 features : Bolt12InvoiceFeatures ,
626635 signing_pubkey : PublicKey ,
636+ #[ cfg( test) ]
637+ experimental_baz : Option < u64 > ,
627638}
628639
629640macro_rules! invoice_accessors { ( $self: ident, $contents: expr) => {
@@ -1208,7 +1219,10 @@ impl InvoiceFields {
12081219 node_id : Some ( & self . signing_pubkey ) ,
12091220 message_paths : None ,
12101221 } ,
1211- ExperimentalInvoiceTlvStreamRef { } ,
1222+ ExperimentalInvoiceTlvStreamRef {
1223+ #[ cfg( test) ]
1224+ experimental_baz : self . experimental_baz ,
1225+ } ,
12121226 )
12131227 }
12141228}
@@ -1297,12 +1311,20 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
12971311} ) ;
12981312
12991313/// Valid type range for experimental invoice TLV records.
1300- const EXPERIMENTAL_INVOICE_TYPES : core:: ops:: RangeFrom < u64 > = 3_000_000_000 ..;
1314+ pub ( super ) const EXPERIMENTAL_INVOICE_TYPES : core:: ops:: RangeFrom < u64 > = 3_000_000_000 ..;
13011315
1316+ #[ cfg( not( test) ) ]
13021317tlv_stream ! (
13031318 ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , { }
13041319) ;
13051320
1321+ #[ cfg( test) ]
1322+ tlv_stream ! (
1323+ ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , {
1324+ ( 3_999_999_999 , experimental_baz: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
1325+ }
1326+ ) ;
1327+
13061328pub ( super ) type BlindedPathIter < ' a > = core:: iter:: Map <
13071329 core:: slice:: Iter < ' a , ( BlindedPayInfo , BlindedPath ) > ,
13081330 for <' r > fn ( & ' r ( BlindedPayInfo , BlindedPath ) ) -> & ' r BlindedPath ,
@@ -1475,7 +1497,10 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14751497 } ,
14761498 experimental_offer_tlv_stream,
14771499 experimental_invoice_request_tlv_stream,
1478- ExperimentalInvoiceTlvStream { } ,
1500+ ExperimentalInvoiceTlvStream {
1501+ #[ cfg( test) ]
1502+ experimental_baz,
1503+ } ,
14791504 ) = tlv_stream;
14801505
14811506 if message_paths. is_some ( ) { return Err ( Bolt12SemanticError :: UnexpectedPaths ) }
@@ -1502,6 +1527,8 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
15021527 let fields = InvoiceFields {
15031528 payment_paths, created_at, relative_expiry, payment_hash, amount_msats, fallbacks,
15041529 features, signing_pubkey,
1530+ #[ cfg( test) ]
1531+ experimental_baz,
15051532 } ;
15061533
15071534 check_invoice_signing_pubkey ( & fields. signing_pubkey , & offer_tlv_stream) ?;
@@ -1567,7 +1594,7 @@ pub(super) fn check_invoice_signing_pubkey(
15671594
15681595#[ cfg( test) ]
15691596mod tests {
1570- use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , ExperimentalInvoiceTlvStreamRef , FallbackAddress , FullInvoiceTlvStreamRef , INVOICE_TYPES , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
1597+ use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , EXPERIMENTAL_INVOICE_TYPES , ExperimentalInvoiceTlvStreamRef , FallbackAddress , FullInvoiceTlvStreamRef , INVOICE_TYPES , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
15711598
15721599 use bitcoin:: { WitnessProgram , WitnessVersion } ;
15731600 use bitcoin:: blockdata:: constants:: ChainHash ;
@@ -1586,7 +1613,7 @@ mod tests {
15861613 use crate :: ln:: inbound_payment:: ExpandedKey ;
15871614 use crate :: ln:: msgs:: DecodeError ;
15881615 use crate :: offers:: invoice_request:: { ExperimentalInvoiceRequestTlvStreamRef , InvoiceRequestTlvStreamRef } ;
1589- use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , TaggedHash , self } ;
1616+ use crate :: offers:: merkle:: { SignError , SignatureTlvStreamRef , TaggedHash , TlvStream , self } ;
15901617 use crate :: offers:: nonce:: Nonce ;
15911618 use crate :: offers:: offer:: { Amount , ExperimentalOfferTlvStreamRef , OfferTlvStreamRef , Quantity } ;
15921619 use crate :: prelude:: * ;
@@ -1762,7 +1789,9 @@ mod tests {
17621789 ExperimentalInvoiceRequestTlvStreamRef {
17631790 experimental_bar: None ,
17641791 } ,
1765- ExperimentalInvoiceTlvStreamRef { } ,
1792+ ExperimentalInvoiceTlvStreamRef {
1793+ experimental_baz: None ,
1794+ } ,
17661795 ) ,
17671796 ) ;
17681797
@@ -1862,7 +1891,9 @@ mod tests {
18621891 ExperimentalInvoiceRequestTlvStreamRef {
18631892 experimental_bar: None ,
18641893 } ,
1865- ExperimentalInvoiceTlvStreamRef { } ,
1894+ ExperimentalInvoiceTlvStreamRef {
1895+ experimental_baz: None ,
1896+ } ,
18661897 ) ,
18671898 ) ;
18681899
@@ -2713,6 +2744,97 @@ mod tests {
27132744 }
27142745 }
27152746
2747+ #[ test]
2748+ fn parses_invoice_with_experimental_tlv_records ( ) {
2749+ let secp_ctx = Secp256k1 :: new ( ) ;
2750+ let keys = Keypair :: from_secret_key ( & secp_ctx, & SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ) ;
2751+ let invoice = OfferBuilder :: new ( keys. public_key ( ) )
2752+ . amount_msats ( 1000 )
2753+ . build ( ) . unwrap ( )
2754+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2755+ . build ( ) . unwrap ( )
2756+ . sign ( payer_sign) . unwrap ( )
2757+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2758+ . experimental_baz ( 42 )
2759+ . build ( ) . unwrap ( )
2760+ . sign ( |message : & UnsignedBolt12Invoice |
2761+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2762+ )
2763+ . unwrap ( ) ;
2764+
2765+ let mut buffer = Vec :: new ( ) ;
2766+ invoice. write ( & mut buffer) . unwrap ( ) ;
2767+
2768+ assert ! ( Bolt12Invoice :: try_from( buffer) . is_ok( ) ) ;
2769+
2770+ const UNKNOWN_ODD_TYPE : u64 = EXPERIMENTAL_INVOICE_TYPES . start + 1 ;
2771+ assert ! ( UNKNOWN_ODD_TYPE % 2 == 1 ) ;
2772+
2773+ let mut unsigned_invoice = OfferBuilder :: new ( keys. public_key ( ) )
2774+ . amount_msats ( 1000 )
2775+ . build ( ) . unwrap ( )
2776+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2777+ . build ( ) . unwrap ( )
2778+ . sign ( payer_sign) . unwrap ( )
2779+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2780+ . build ( ) . unwrap ( ) ;
2781+
2782+ BigSize ( UNKNOWN_ODD_TYPE ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2783+ BigSize ( 32 ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2784+ [ 42u8 ; 32 ] . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2785+
2786+ let tlv_stream = TlvStream :: new ( & unsigned_invoice. bytes )
2787+ . chain ( TlvStream :: new ( & unsigned_invoice. experimental_bytes ) ) ;
2788+ unsigned_invoice. tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
2789+
2790+ let invoice = unsigned_invoice
2791+ . sign ( |message : & UnsignedBolt12Invoice |
2792+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2793+ )
2794+ . unwrap ( ) ;
2795+
2796+ let mut encoded_invoice = Vec :: new ( ) ;
2797+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2798+
2799+ if let Err ( e) = Bolt12Invoice :: try_from ( encoded_invoice) {
2800+ panic ! ( "error parsing invoice: {:?}" , e) ;
2801+ }
2802+
2803+ const UNKNOWN_EVEN_TYPE : u64 = EXPERIMENTAL_INVOICE_TYPES . start ;
2804+ assert ! ( UNKNOWN_EVEN_TYPE % 2 == 0 ) ;
2805+
2806+ let mut unsigned_invoice = OfferBuilder :: new ( keys. public_key ( ) )
2807+ . amount_msats ( 1000 )
2808+ . build ( ) . unwrap ( )
2809+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2810+ . build ( ) . unwrap ( )
2811+ . sign ( payer_sign) . unwrap ( )
2812+ . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2813+ . build ( ) . unwrap ( ) ;
2814+
2815+ BigSize ( UNKNOWN_EVEN_TYPE ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2816+ BigSize ( 32 ) . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2817+ [ 42u8 ; 32 ] . write ( & mut unsigned_invoice. experimental_bytes ) . unwrap ( ) ;
2818+
2819+ let tlv_stream = TlvStream :: new ( & unsigned_invoice. bytes )
2820+ . chain ( TlvStream :: new ( & unsigned_invoice. experimental_bytes ) ) ;
2821+ unsigned_invoice. tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
2822+
2823+ let invoice = unsigned_invoice
2824+ . sign ( |message : & UnsignedBolt12Invoice |
2825+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2826+ )
2827+ . unwrap ( ) ;
2828+
2829+ let mut encoded_invoice = Vec :: new ( ) ;
2830+ invoice. write ( & mut encoded_invoice) . unwrap ( ) ;
2831+
2832+ match Bolt12Invoice :: try_from ( encoded_invoice) {
2833+ Ok ( _) => panic ! ( "expected error" ) ,
2834+ Err ( e) => assert_eq ! ( e, Bolt12ParseError :: Decode ( DecodeError :: UnknownRequiredFeature ) ) ,
2835+ }
2836+ }
2837+
27162838 #[ test]
27172839 fn fails_parsing_invoice_with_out_of_range_tlv_records ( ) {
27182840 let invoice = OfferBuilder :: new ( recipient_pubkey ( ) )
0 commit comments