@@ -121,10 +121,10 @@ use crate::ln::msgs::DecodeError;
121
121
use crate :: offers:: invoice_macros:: { invoice_accessors_common, invoice_builder_methods_common} ;
122
122
#[ cfg( test) ]
123
123
use 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 } ;
126
126
use 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 } ;
128
128
use crate :: offers:: parse:: { Bolt12ParseError , Bolt12SemanticError , ParsedMessage } ;
129
129
use crate :: offers:: payer:: { PAYER_METADATA_TYPE , PayerTlvStream , PayerTlvStreamRef } ;
130
130
use 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> {
461
461
#[ derive( Clone ) ]
462
462
pub struct UnsignedBolt12Invoice {
463
463
bytes : Vec < u8 > ,
464
+ experimental_bytes : Vec < u8 > ,
464
465
contents : InvoiceContents ,
465
466
tagged_hash : TaggedHash ,
466
467
}
@@ -491,19 +492,57 @@ where
491
492
492
493
impl UnsignedBolt12Invoice {
493
494
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
+
494
515
// Use the invoice_request bytes instead of the invoice_request TLV stream as the latter may
495
516
// have contained unknown TLV records, which are not stored in `InvoiceRequestContents` or
496
517
// `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
+ }
500
521
501
- let mut bytes = Vec :: new ( ) ;
502
- unsigned_tlv_stream. write ( & mut bytes) . unwrap ( ) ;
522
+ let remaining_bytes = & invreq_bytes[ bytes. len ( ) ..] ;
503
523
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
+ ) ;
535
+
536
+ for record in experimental_tlv_stream {
537
+ record. write ( & mut experimental_bytes) . unwrap ( ) ;
538
+ }
505
539
506
- Self { bytes, contents, tagged_hash }
540
+ debug_assert_eq ! ( experimental_bytes. len( ) , experimental_bytes. capacity( ) ) ;
541
+
542
+ let tlv_stream = TlvStream :: new ( & bytes) . chain ( TlvStream :: new ( & experimental_bytes) ) ;
543
+ let tagged_hash = TaggedHash :: from_tlv_stream ( SIGNATURE_TAG , tlv_stream) ;
544
+
545
+ Self { bytes, experimental_bytes, contents, tagged_hash }
507
546
}
508
547
509
548
/// Returns the [`TaggedHash`] of the invoice to sign.
@@ -528,6 +567,17 @@ macro_rules! unsigned_invoice_sign_method { ($self: ident, $self_type: ty $(, $s
528
567
} ;
529
568
signature_tlv_stream. write( & mut $self. bytes) . unwrap( ) ;
530
569
570
+ // Append the experimental bytes after the signature.
571
+ debug_assert_eq!(
572
+ // The two-byte overallocation results from SIGNATURE_TLV_RECORD_SIZE accommodating TLV
573
+ // records with types >= 253.
574
+ $self. bytes. len( )
575
+ + $self. experimental_bytes. len( )
576
+ + if $self. contents. is_for_offer( ) { 0 } else { 2 } ,
577
+ $self. bytes. capacity( ) ,
578
+ ) ;
579
+ $self. bytes. extend_from_slice( & $self. experimental_bytes) ;
580
+
531
581
Ok ( Bolt12Invoice {
532
582
#[ cfg( not( c_bindings) ) ]
533
583
bytes: $self. bytes,
@@ -882,6 +932,13 @@ impl Hash for Bolt12Invoice {
882
932
}
883
933
884
934
impl InvoiceContents {
935
+ fn is_for_offer ( & self ) -> bool {
936
+ match self {
937
+ InvoiceContents :: ForOffer { .. } => true ,
938
+ InvoiceContents :: ForRefund { .. } => false ,
939
+ }
940
+ }
941
+
885
942
/// Whether the original offer or refund has expired.
886
943
#[ cfg( feature = "std" ) ]
887
944
fn is_offer_or_refund_expired ( & self ) -> bool {
@@ -1211,7 +1268,7 @@ impl TryFrom<Vec<u8>> for UnsignedBolt12Invoice {
1211
1268
1212
1269
fn try_from ( bytes : Vec < u8 > ) -> Result < Self , Self :: Error > {
1213
1270
let invoice = ParsedMessage :: < PartialInvoiceTlvStream > :: try_from ( bytes) ?;
1214
- let ParsedMessage { bytes, tlv_stream } = invoice;
1271
+ let ParsedMessage { mut bytes, tlv_stream } = invoice;
1215
1272
let (
1216
1273
payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
1217
1274
) = tlv_stream;
@@ -1221,7 +1278,13 @@ impl TryFrom<Vec<u8>> for UnsignedBolt12Invoice {
1221
1278
1222
1279
let tagged_hash = TaggedHash :: from_valid_tlv_stream_bytes ( SIGNATURE_TAG , & bytes) ;
1223
1280
1224
- Ok ( UnsignedBolt12Invoice { bytes, contents, tagged_hash } )
1281
+ let offset = TlvStream :: new ( & bytes)
1282
+ . range ( 0 ..INVOICE_TYPES . end )
1283
+ . last ( )
1284
+ . map_or ( 0 , |last_record| last_record. end ) ;
1285
+ let experimental_bytes = bytes. split_off ( offset) ;
1286
+
1287
+ Ok ( UnsignedBolt12Invoice { bytes, experimental_bytes, contents, tagged_hash } )
1225
1288
}
1226
1289
}
1227
1290
@@ -2512,10 +2575,15 @@ mod tests {
2512
2575
. respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2513
2576
. build ( ) . unwrap ( ) ;
2514
2577
2515
- BigSize ( UNKNOWN_ODD_TYPE ) . write ( & mut unsigned_invoice. bytes ) . unwrap ( ) ;
2516
- BigSize ( 32 ) . write ( & mut unsigned_invoice. bytes ) . unwrap ( ) ;
2517
- [ 42u8 ; 32 ] . write ( & mut unsigned_invoice. bytes ) . unwrap ( ) ;
2578
+ let mut unknown_bytes = Vec :: new ( ) ;
2579
+ BigSize ( UNKNOWN_ODD_TYPE ) . write ( & mut unknown_bytes) . unwrap ( ) ;
2580
+ BigSize ( 32 ) . write ( & mut unknown_bytes) . unwrap ( ) ;
2581
+ [ 42u8 ; 32 ] . write ( & mut unknown_bytes) . unwrap ( ) ;
2518
2582
2583
+ unsigned_invoice. bytes . reserve_exact (
2584
+ unsigned_invoice. bytes . capacity ( ) - unsigned_invoice. bytes . len ( ) + unknown_bytes. len ( ) ,
2585
+ ) ;
2586
+ unsigned_invoice. bytes . extend_from_slice ( & unknown_bytes) ;
2519
2587
unsigned_invoice. tagged_hash =
2520
2588
TaggedHash :: from_valid_tlv_stream_bytes ( SIGNATURE_TAG , & unsigned_invoice. bytes ) ;
2521
2589
@@ -2545,10 +2613,15 @@ mod tests {
2545
2613
. respond_with_no_std ( payment_paths ( ) , payment_hash ( ) , now ( ) ) . unwrap ( )
2546
2614
. build ( ) . unwrap ( ) ;
2547
2615
2548
- BigSize ( UNKNOWN_EVEN_TYPE ) . write ( & mut unsigned_invoice. bytes ) . unwrap ( ) ;
2549
- BigSize ( 32 ) . write ( & mut unsigned_invoice. bytes ) . unwrap ( ) ;
2550
- [ 42u8 ; 32 ] . write ( & mut unsigned_invoice. bytes ) . unwrap ( ) ;
2616
+ let mut unknown_bytes = Vec :: new ( ) ;
2617
+ BigSize ( UNKNOWN_EVEN_TYPE ) . write ( & mut unknown_bytes) . unwrap ( ) ;
2618
+ BigSize ( 32 ) . write ( & mut unknown_bytes) . unwrap ( ) ;
2619
+ [ 42u8 ; 32 ] . write ( & mut unknown_bytes) . unwrap ( ) ;
2551
2620
2621
+ unsigned_invoice. bytes . reserve_exact (
2622
+ unsigned_invoice. bytes . capacity ( ) - unsigned_invoice. bytes . len ( ) + unknown_bytes. len ( ) ,
2623
+ ) ;
2624
+ unsigned_invoice. bytes . extend_from_slice ( & unknown_bytes) ;
2552
2625
unsigned_invoice. tagged_hash =
2553
2626
TaggedHash :: from_valid_tlv_stream_bytes ( SIGNATURE_TAG , & unsigned_invoice. bytes ) ;
2554
2627
0 commit comments