@@ -497,7 +497,8 @@ impl UnsignedBolt12Invoice {
497497 const EXPERIMENTAL_TYPES : core:: ops:: Range < u64 > =
498498 EXPERIMENTAL_OFFER_TYPES . start ..EXPERIMENTAL_INVOICE_REQUEST_TYPES . end ;
499499
500- let ( _, _, _, invoice_tlv_stream, _, _) = contents. as_tlv_stream ( ) ;
500+ let ( _, _, _, invoice_tlv_stream, _, _, experimental_invoice_tlv_stream) =
501+ contents. as_tlv_stream ( ) ;
501502
502503 // Allocate enough space for the invoice, which will include:
503504 // - all TLV records from `invreq_bytes` except signatures,
@@ -510,6 +511,7 @@ impl UnsignedBolt12Invoice {
510511 invreq_bytes. len ( )
511512 + invoice_tlv_stream. serialized_length ( )
512513 + if contents. is_for_offer ( ) { 0 } else { SIGNATURE_TLV_RECORD_SIZE }
514+ + experimental_invoice_tlv_stream. serialized_length ( ) ,
513515 ) ;
514516
515517 // Use the invoice_request bytes instead of the invoice_request TLV stream as the latter may
@@ -531,12 +533,15 @@ impl UnsignedBolt12Invoice {
531533 - experimental_tlv_stream
532534 . peek ( )
533535 . map_or ( remaining_bytes. len ( ) , |first_record| first_record. start )
536+ + experimental_invoice_tlv_stream. serialized_length ( ) ,
534537 ) ;
535538
536539 for record in experimental_tlv_stream {
537540 record. write ( & mut experimental_bytes) . unwrap ( ) ;
538541 }
539542
543+ experimental_invoice_tlv_stream. write ( & mut experimental_bytes) . unwrap ( ) ;
544+
540545 let tlv_stream = TlvStream :: new ( & bytes) . chain ( TlvStream :: new ( & experimental_bytes) ) ;
541546 let tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
542547
@@ -894,14 +899,15 @@ impl Bolt12Invoice {
894899 let (
895900 payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
896901 experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
902+ experimental_invoice_tlv_stream,
897903 ) = self . contents . as_tlv_stream ( ) ;
898904 let signature_tlv_stream = SignatureTlvStreamRef {
899905 signature : Some ( & self . signature ) ,
900906 } ;
901907 (
902908 payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
903909 signature_tlv_stream, experimental_offer_tlv_stream,
904- experimental_invoice_request_tlv_stream,
910+ experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream ,
905911 )
906912 }
907913
@@ -1169,9 +1175,12 @@ impl InvoiceContents {
11691175 InvoiceContents :: ForOffer { invoice_request, .. } => invoice_request. as_tlv_stream ( ) ,
11701176 InvoiceContents :: ForRefund { refund, .. } => refund. as_tlv_stream ( ) ,
11711177 } ;
1172- let invoice = self . fields ( ) . as_tlv_stream ( ) ;
1178+ let ( invoice, experimental_invoice ) = self . fields ( ) . as_tlv_stream ( ) ;
11731179
1174- ( payer, offer, invoice_request, invoice, experimental_offer, experimental_invoice_request)
1180+ (
1181+ payer, offer, invoice_request, invoice, experimental_offer,
1182+ experimental_invoice_request, experimental_invoice,
1183+ )
11751184 }
11761185}
11771186
@@ -1219,24 +1228,27 @@ pub(super) fn filter_fallbacks(
12191228}
12201229
12211230impl InvoiceFields {
1222- fn as_tlv_stream ( & self ) -> InvoiceTlvStreamRef {
1231+ fn as_tlv_stream ( & self ) -> ( InvoiceTlvStreamRef , ExperimentalInvoiceTlvStreamRef ) {
12231232 let features = {
12241233 if self . features == Bolt12InvoiceFeatures :: empty ( ) { None }
12251234 else { Some ( & self . features ) }
12261235 } ;
12271236
1228- InvoiceTlvStreamRef {
1229- paths : Some ( Iterable ( self . payment_paths . iter ( ) . map ( |path| path. inner_blinded_path ( ) ) ) ) ,
1230- blindedpay : Some ( Iterable ( self . payment_paths . iter ( ) . map ( |path| & path. payinfo ) ) ) ,
1231- created_at : Some ( self . created_at . as_secs ( ) ) ,
1232- relative_expiry : self . relative_expiry . map ( |duration| duration. as_secs ( ) as u32 ) ,
1233- payment_hash : Some ( & self . payment_hash ) ,
1234- amount : Some ( self . amount_msats ) ,
1235- fallbacks : self . fallbacks . as_ref ( ) ,
1236- features,
1237- node_id : Some ( & self . signing_pubkey ) ,
1238- message_paths : None ,
1239- }
1237+ (
1238+ InvoiceTlvStreamRef {
1239+ paths : Some ( Iterable ( self . payment_paths . iter ( ) . map ( |path| path. inner_blinded_path ( ) ) ) ) ,
1240+ blindedpay : Some ( Iterable ( self . payment_paths . iter ( ) . map ( |path| & path. payinfo ) ) ) ,
1241+ created_at : Some ( self . created_at . as_secs ( ) ) ,
1242+ relative_expiry : self . relative_expiry . map ( |duration| duration. as_secs ( ) as u32 ) ,
1243+ payment_hash : Some ( & self . payment_hash ) ,
1244+ amount : Some ( self . amount_msats ) ,
1245+ fallbacks : self . fallbacks . as_ref ( ) ,
1246+ features,
1247+ node_id : Some ( & self . signing_pubkey ) ,
1248+ message_paths : None ,
1249+ } ,
1250+ ExperimentalInvoiceTlvStreamRef { } ,
1251+ )
12401252 }
12411253}
12421254
@@ -1311,6 +1323,13 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
13111323 ( 236 , message_paths: ( Vec <BlindedMessagePath >, WithoutLength ) ) ,
13121324} ) ;
13131325
1326+ /// Valid type range for experimental invoice TLV records.
1327+ const EXPERIMENTAL_INVOICE_TYPES : core:: ops:: RangeFrom < u64 > = 3_000_000_000 ..;
1328+
1329+ tlv_stream ! (
1330+ ExperimentalInvoiceTlvStream , ExperimentalInvoiceTlvStreamRef , EXPERIMENTAL_INVOICE_TYPES , { }
1331+ ) ;
1332+
13141333pub ( super ) type BlindedPathIter < ' a > = core:: iter:: Map <
13151334 core:: slice:: Iter < ' a , BlindedPaymentPath > ,
13161335 for <' r > fn ( & ' r BlindedPaymentPath ) -> & ' r BlindedPath ,
@@ -1332,7 +1351,7 @@ impl_writeable!(FallbackAddress, { version, program });
13321351
13331352type FullInvoiceTlvStream =(
13341353 PayerTlvStream , OfferTlvStream , InvoiceRequestTlvStream , InvoiceTlvStream , SignatureTlvStream ,
1335- ExperimentalOfferTlvStream , ExperimentalInvoiceRequestTlvStream ,
1354+ ExperimentalOfferTlvStream , ExperimentalInvoiceRequestTlvStream , ExperimentalInvoiceTlvStream ,
13361355) ;
13371356
13381357type FullInvoiceTlvStreamRef < ' a > = (
@@ -1343,6 +1362,7 @@ type FullInvoiceTlvStreamRef<'a> = (
13431362 SignatureTlvStreamRef < ' a > ,
13441363 ExperimentalOfferTlvStreamRef ,
13451364 ExperimentalInvoiceRequestTlvStreamRef ,
1365+ ExperimentalInvoiceTlvStreamRef ,
13461366) ;
13471367
13481368impl CursorReadable for FullInvoiceTlvStream {
@@ -1354,19 +1374,20 @@ impl CursorReadable for FullInvoiceTlvStream {
13541374 let signature = CursorReadable :: read ( r) ?;
13551375 let experimental_offer = CursorReadable :: read ( r) ?;
13561376 let experimental_invoice_request = CursorReadable :: read ( r) ?;
1377+ let experimental_invoice = CursorReadable :: read ( r) ?;
13571378
13581379 Ok (
13591380 (
13601381 payer, offer, invoice_request, invoice, signature, experimental_offer,
1361- experimental_invoice_request,
1382+ experimental_invoice_request, experimental_invoice ,
13621383 )
13631384 )
13641385 }
13651386}
13661387
13671388type PartialInvoiceTlvStream = (
13681389 PayerTlvStream , OfferTlvStream , InvoiceRequestTlvStream , InvoiceTlvStream ,
1369- ExperimentalOfferTlvStream , ExperimentalInvoiceRequestTlvStream ,
1390+ ExperimentalOfferTlvStream , ExperimentalInvoiceRequestTlvStream , ExperimentalInvoiceTlvStream ,
13701391) ;
13711392
13721393type PartialInvoiceTlvStreamRef < ' a > = (
@@ -1376,6 +1397,7 @@ type PartialInvoiceTlvStreamRef<'a> = (
13761397 InvoiceTlvStreamRef < ' a > ,
13771398 ExperimentalOfferTlvStreamRef ,
13781399 ExperimentalInvoiceRequestTlvStreamRef ,
1400+ ExperimentalInvoiceTlvStreamRef ,
13791401) ;
13801402
13811403impl CursorReadable for PartialInvoiceTlvStream {
@@ -1386,11 +1408,12 @@ impl CursorReadable for PartialInvoiceTlvStream {
13861408 let invoice = CursorReadable :: read ( r) ?;
13871409 let experimental_offer = CursorReadable :: read ( r) ?;
13881410 let experimental_invoice_request = CursorReadable :: read ( r) ?;
1411+ let experimental_invoice = CursorReadable :: read ( r) ?;
13891412
13901413 Ok (
13911414 (
13921415 payer, offer, invoice_request, invoice, experimental_offer,
1393- experimental_invoice_request,
1416+ experimental_invoice_request, experimental_invoice ,
13941417 )
13951418 )
13961419 }
@@ -1406,11 +1429,13 @@ impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for Bolt12Invoice {
14061429 SignatureTlvStream { signature } ,
14071430 experimental_offer_tlv_stream,
14081431 experimental_invoice_request_tlv_stream,
1432+ experimental_invoice_tlv_stream,
14091433 ) = tlv_stream;
14101434 let contents = InvoiceContents :: try_from (
14111435 (
14121436 payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
14131437 experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
1438+ experimental_invoice_tlv_stream,
14141439 )
14151440 ) ?;
14161441
@@ -1439,6 +1464,7 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
14391464 } ,
14401465 experimental_offer_tlv_stream,
14411466 experimental_invoice_request_tlv_stream,
1467+ ExperimentalInvoiceTlvStream { } ,
14421468 ) = tlv_stream;
14431469
14441470 if message_paths. is_some ( ) { return Err ( Bolt12SemanticError :: UnexpectedPaths ) }
@@ -1535,7 +1561,7 @@ pub(super) fn check_invoice_signing_pubkey(
15351561
15361562#[ cfg( test) ]
15371563mod tests {
1538- use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , FallbackAddress , FullInvoiceTlvStreamRef , INVOICE_TYPES , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
1564+ use super :: { Bolt12Invoice , DEFAULT_RELATIVE_EXPIRY , ExperimentalInvoiceTlvStreamRef , FallbackAddress , FullInvoiceTlvStreamRef , INVOICE_TYPES , InvoiceTlvStreamRef , SIGNATURE_TAG , UnsignedBolt12Invoice } ;
15391565
15401566 use bitcoin:: { CompressedPublicKey , WitnessProgram , WitnessVersion } ;
15411567 use bitcoin:: constants:: ChainHash ;
@@ -1731,6 +1757,7 @@ mod tests {
17311757 ExperimentalInvoiceRequestTlvStreamRef {
17321758 experimental_bar: None ,
17331759 } ,
1760+ ExperimentalInvoiceTlvStreamRef { } ,
17341761 ) ,
17351762 ) ;
17361763
@@ -1830,6 +1857,7 @@ mod tests {
18301857 ExperimentalInvoiceRequestTlvStreamRef {
18311858 experimental_bar: None ,
18321859 } ,
1860+ ExperimentalInvoiceTlvStreamRef { } ,
18331861 ) ,
18341862 ) ;
18351863
@@ -2026,7 +2054,7 @@ mod tests {
20262054 . relative_expiry ( one_hour. as_secs ( ) as u32 )
20272055 . build ( ) . unwrap ( )
20282056 . sign ( recipient_sign) . unwrap ( ) ;
2029- let ( _, _, _, tlv_stream, _, _, _) = invoice. as_tlv_stream ( ) ;
2057+ let ( _, _, _, tlv_stream, _, _, _, _ ) = invoice. as_tlv_stream ( ) ;
20302058 #[ cfg( feature = "std" ) ]
20312059 assert ! ( !invoice. is_expired( ) ) ;
20322060 assert_eq ! ( invoice. relative_expiry( ) , one_hour) ;
@@ -2042,7 +2070,7 @@ mod tests {
20422070 . relative_expiry ( one_hour. as_secs ( ) as u32 - 1 )
20432071 . build ( ) . unwrap ( )
20442072 . sign ( recipient_sign) . unwrap ( ) ;
2045- let ( _, _, _, tlv_stream, _, _, _) = invoice. as_tlv_stream ( ) ;
2073+ let ( _, _, _, tlv_stream, _, _, _, _ ) = invoice. as_tlv_stream ( ) ;
20462074 #[ cfg( feature = "std" ) ]
20472075 assert ! ( invoice. is_expired( ) ) ;
20482076 assert_eq ! ( invoice. relative_expiry( ) , one_hour - Duration :: from_secs( 1 ) ) ;
@@ -2061,7 +2089,7 @@ mod tests {
20612089 . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
20622090 . build ( ) . unwrap ( )
20632091 . sign ( recipient_sign) . unwrap ( ) ;
2064- let ( _, _, _, tlv_stream, _, _, _) = invoice. as_tlv_stream ( ) ;
2092+ let ( _, _, _, tlv_stream, _, _, _, _ ) = invoice. as_tlv_stream ( ) ;
20652093 assert_eq ! ( invoice. amount_msats( ) , 1001 ) ;
20662094 assert_eq ! ( tlv_stream. amount, Some ( 1001 ) ) ;
20672095 }
@@ -2079,7 +2107,7 @@ mod tests {
20792107 . respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
20802108 . build ( ) . unwrap ( )
20812109 . sign ( recipient_sign) . unwrap ( ) ;
2082- let ( _, _, _, tlv_stream, _, _, _) = invoice. as_tlv_stream ( ) ;
2110+ let ( _, _, _, tlv_stream, _, _, _, _ ) = invoice. as_tlv_stream ( ) ;
20832111 assert_eq ! ( invoice. amount_msats( ) , 2000 ) ;
20842112 assert_eq ! ( tlv_stream. amount, Some ( 2000 ) ) ;
20852113
@@ -2117,7 +2145,7 @@ mod tests {
21172145 . fallback_v1_p2tr_tweaked ( & tweaked_pubkey)
21182146 . build ( ) . unwrap ( )
21192147 . sign ( recipient_sign) . unwrap ( ) ;
2120- let ( _, _, _, tlv_stream, _, _, _) = invoice. as_tlv_stream ( ) ;
2148+ let ( _, _, _, tlv_stream, _, _, _, _ ) = invoice. as_tlv_stream ( ) ;
21212149 assert_eq ! (
21222150 invoice. fallbacks( ) ,
21232151 vec![
@@ -2160,7 +2188,7 @@ mod tests {
21602188 . allow_mpp ( )
21612189 . build ( ) . unwrap ( )
21622190 . sign ( recipient_sign) . unwrap ( ) ;
2163- let ( _, _, _, tlv_stream, _, _, _) = invoice. as_tlv_stream ( ) ;
2191+ let ( _, _, _, tlv_stream, _, _, _, _ ) = invoice. as_tlv_stream ( ) ;
21642192 assert_eq ! ( invoice. invoice_features( ) , & features) ;
21652193 assert_eq ! ( tlv_stream. features, Some ( & features) ) ;
21662194 }
0 commit comments