@@ -121,10 +121,10 @@ use crate::ln::msgs::DecodeError;
121121use crate :: offers:: invoice_macros:: { invoice_accessors_common, invoice_builder_methods_common} ;
122122#[ cfg( test) ]
123123use crate :: offers:: invoice_macros:: invoice_builder_methods_test;
124- use crate :: offers:: invoice_request:: { INVOICE_REQUEST_PAYER_ID_TYPE , INVOICE_REQUEST_TYPES , IV_BYTES as INVOICE_REQUEST_IV_BYTES , InvoiceRequest , InvoiceRequestContents , InvoiceRequestTlvStream , InvoiceRequestTlvStreamRef } ;
125- use crate :: offers:: merkle:: { SignError , SignFn , SignatureTlvStream , SignatureTlvStreamRef , TaggedHash , TlvStream , WithoutSignatures , self } ;
124+ use crate :: offers:: invoice_request:: { EXPERIMENTAL_INVOICE_REQUEST_TYPES , INVOICE_REQUEST_PAYER_ID_TYPE , INVOICE_REQUEST_TYPES , IV_BYTES as INVOICE_REQUEST_IV_BYTES , InvoiceRequest , InvoiceRequestContents , InvoiceRequestTlvStream , InvoiceRequestTlvStreamRef } ;
125+ use crate :: offers:: merkle:: { SignError , SignFn , SignatureTlvStream , SignatureTlvStreamRef , TaggedHash , TlvStream , self , SIGNATURE_TLV_RECORD_SIZE } ;
126126use crate :: offers:: nonce:: Nonce ;
127- use crate :: offers:: offer:: { Amount , OFFER_TYPES , OfferTlvStream , OfferTlvStreamRef , Quantity } ;
127+ use crate :: offers:: offer:: { Amount , EXPERIMENTAL_OFFER_TYPES , OFFER_TYPES , OfferTlvStream , OfferTlvStreamRef , Quantity } ;
128128use crate :: offers:: parse:: { Bolt12ParseError , Bolt12SemanticError , ParsedMessage } ;
129129use crate :: offers:: payer:: { PAYER_METADATA_TYPE , PayerTlvStream , PayerTlvStreamRef } ;
130130use crate :: offers:: refund:: { IV_BYTES_WITH_METADATA as REFUND_IV_BYTES_WITH_METADATA , IV_BYTES_WITHOUT_METADATA as REFUND_IV_BYTES_WITHOUT_METADATA , Refund , RefundContents } ;
@@ -461,6 +461,7 @@ for InvoiceBuilder<'a, DerivedSigningPubkey> {
461461#[ derive( Clone ) ]
462462pub struct UnsignedBolt12Invoice {
463463 bytes : Vec < u8 > ,
464+ experimental_bytes : Vec < u8 > ,
464465 contents : InvoiceContents ,
465466 tagged_hash : TaggedHash ,
466467}
@@ -491,19 +492,55 @@ where
491492
492493impl UnsignedBolt12Invoice {
493494 fn new ( invreq_bytes : & [ u8 ] , contents : InvoiceContents ) -> Self {
495+ // TLV record ranges applicable to invreq_bytes.
496+ const NON_EXPERIMENTAL_TYPES : core:: ops:: Range < u64 > = 0 ..INVOICE_REQUEST_TYPES . end ;
497+ const EXPERIMENTAL_TYPES : core:: ops:: Range < u64 > =
498+ EXPERIMENTAL_OFFER_TYPES . start ..EXPERIMENTAL_INVOICE_REQUEST_TYPES . end ;
499+
500+ let ( _, _, _, invoice_tlv_stream) = contents. as_tlv_stream ( ) ;
501+
502+ // Allocate enough space for the invoice, which will include:
503+ // - all TLV records from `invreq_bytes` except signatures,
504+ // - all invoice-specific TLV records, and
505+ // - a signature TLV record once the invoice is signed.
506+ //
507+ // This assumes both the invoice request and the invoice will each only have one signature
508+ // using SIGNATURE_TYPES.start as the TLV record. Thus, it is accounted for by invreq_bytes.
509+ let mut bytes = Vec :: with_capacity (
510+ invreq_bytes. len ( )
511+ + invoice_tlv_stream. serialized_length ( )
512+ + if contents. is_for_offer ( ) { 0 } else { SIGNATURE_TLV_RECORD_SIZE }
513+ ) ;
514+
494515 // Use the invoice_request bytes instead of the invoice_request TLV stream as the latter may
495516 // have contained unknown TLV records, which are not stored in `InvoiceRequestContents` or
496517 // `RefundContents`.
497- let ( _ , _ , _ , invoice_tlv_stream ) = contents . as_tlv_stream ( ) ;
498- let invoice_request_bytes = WithoutSignatures ( invreq_bytes ) ;
499- let unsigned_tlv_stream = ( invoice_request_bytes , invoice_tlv_stream ) ;
518+ for record in TlvStream :: new ( invreq_bytes ) . range ( NON_EXPERIMENTAL_TYPES ) {
519+ record . write ( & mut bytes ) . unwrap ( ) ;
520+ }
500521
501- let mut bytes = Vec :: new ( ) ;
502- unsigned_tlv_stream. write ( & mut bytes) . unwrap ( ) ;
522+ let remaining_bytes = & invreq_bytes[ bytes. len ( ) ..] ;
503523
504- let tagged_hash = TaggedHash :: from_valid_tlv_stream_bytes ( SIGNATURE_TAG , & bytes) ;
524+ invoice_tlv_stream. write ( & mut bytes) . unwrap ( ) ;
525+
526+ let mut experimental_tlv_stream = TlvStream :: new ( remaining_bytes)
527+ . range ( EXPERIMENTAL_TYPES )
528+ . peekable ( ) ;
529+ let mut experimental_bytes = Vec :: with_capacity (
530+ remaining_bytes. len ( )
531+ - experimental_tlv_stream
532+ . peek ( )
533+ . map_or ( remaining_bytes. len ( ) , |first_record| first_record. start )
534+ ) ;
505535
506- Self { bytes, contents, tagged_hash }
536+ for record in experimental_tlv_stream {
537+ record. write ( & mut experimental_bytes) . unwrap ( ) ;
538+ }
539+
540+ let tlv_stream = TlvStream :: new ( & bytes) . chain ( TlvStream :: new ( & experimental_bytes) ) ;
541+ let tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
542+
543+ Self { bytes, experimental_bytes, contents, tagged_hash }
507544 }
508545
509546 /// Returns the [`TaggedHash`] of the invoice to sign.
@@ -528,6 +565,9 @@ macro_rules! unsigned_invoice_sign_method { ($self: ident, $self_type: ty $(, $s
528565 } ;
529566 signature_tlv_stream. write( & mut $self. bytes) . unwrap( ) ;
530567
568+ // Append the experimental bytes after the signature.
569+ WithoutLength ( & $self. experimental_bytes) . write( & mut $self. bytes) . unwrap( ) ;
570+
531571 Ok ( Bolt12Invoice {
532572 #[ cfg( not( c_bindings) ) ]
533573 bytes: $self. bytes,
@@ -882,6 +922,13 @@ impl Hash for Bolt12Invoice {
882922}
883923
884924impl InvoiceContents {
925+ fn is_for_offer ( & self ) -> bool {
926+ match self {
927+ InvoiceContents :: ForOffer { .. } => true ,
928+ InvoiceContents :: ForRefund { .. } => false ,
929+ }
930+ }
931+
885932 /// Whether the original offer or refund has expired.
886933 #[ cfg( feature = "std" ) ]
887934 fn is_offer_or_refund_expired ( & self ) -> bool {
@@ -1211,7 +1258,7 @@ impl TryFrom<Vec<u8>> for UnsignedBolt12Invoice {
12111258
12121259 fn try_from ( bytes : Vec < u8 > ) -> Result < Self , Self :: Error > {
12131260 let invoice = ParsedMessage :: < PartialInvoiceTlvStream > :: try_from ( bytes) ?;
1214- let ParsedMessage { bytes, tlv_stream } = invoice;
1261+ let ParsedMessage { mut bytes, tlv_stream } = invoice;
12151262 let (
12161263 payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
12171264 ) = tlv_stream;
@@ -1221,7 +1268,13 @@ impl TryFrom<Vec<u8>> for UnsignedBolt12Invoice {
12211268
12221269 let tagged_hash = TaggedHash :: from_valid_tlv_stream_bytes ( SIGNATURE_TAG , & bytes) ;
12231270
1224- Ok ( UnsignedBolt12Invoice { bytes, contents, tagged_hash } )
1271+ let offset = TlvStream :: new ( & bytes)
1272+ . range ( 0 ..INVOICE_TYPES . end )
1273+ . last ( )
1274+ . map_or ( 0 , |last_record| last_record. end ) ;
1275+ let experimental_bytes = bytes. split_off ( offset) ;
1276+
1277+ Ok ( UnsignedBolt12Invoice { bytes, experimental_bytes, contents, tagged_hash } )
12251278 }
12261279}
12271280
0 commit comments