@@ -431,10 +431,13 @@ pub struct RecipientOnionFields {
431
431
/// [`Self::payment_secret`] and while nearly all lightning senders support secrets, metadata
432
432
/// may not be supported as universally.
433
433
pub payment_metadata : Option < Vec < u8 > > ,
434
+ /// See [`Self::custom_tlvs`] for more info.
435
+ pub ( super ) custom_tlvs : Vec < ( u64 , Vec < u8 > ) > ,
434
436
}
435
437
436
438
impl_writeable_tlv_based ! ( RecipientOnionFields , {
437
439
( 0 , payment_secret, option) ,
440
+ ( 1 , custom_tlvs, optional_vec) ,
438
441
( 2 , payment_metadata, option) ,
439
442
} ) ;
440
443
@@ -443,7 +446,7 @@ impl RecipientOnionFields {
443
446
/// set of onion fields for today's BOLT11 invoices - most nodes require a [`PaymentSecret`]
444
447
/// but do not require or provide any further data.
445
448
pub fn secret_only ( payment_secret : PaymentSecret ) -> Self {
446
- Self { payment_secret : Some ( payment_secret) , payment_metadata : None }
449
+ Self { payment_secret : Some ( payment_secret) , payment_metadata : None , custom_tlvs : Vec :: new ( ) }
447
450
}
448
451
449
452
/// Creates a new [`RecipientOnionFields`] with no fields. This generally does not create
@@ -455,7 +458,46 @@ impl RecipientOnionFields {
455
458
/// [`ChannelManager::send_spontaneous_payment`]: super::channelmanager::ChannelManager::send_spontaneous_payment
456
459
/// [`RecipientOnionFields::secret_only`]: RecipientOnionFields::secret_only
457
460
pub fn spontaneous_empty ( ) -> Self {
458
- Self { payment_secret : None , payment_metadata : None }
461
+ Self { payment_secret : None , payment_metadata : None , custom_tlvs : Vec :: new ( ) }
462
+ }
463
+
464
+ /// Creates a new [`RecipientOnionFields`] from an existing one, adding custom TLVs. Each
465
+ /// TLV is provided as a `(u64, Vec<u8>)` for the type number and serialized value
466
+ /// respectively. TLV type numbers must be unique and within the range
467
+ /// reserved for custom types, i.e. >= 2^16, otherwise this method will return `Err(())`.
468
+ ///
469
+ /// This method will also error for types in the experimental range which have been
470
+ /// standardized within the protocol, which only includes 5482373484 (keysend) for now.
471
+ ///
472
+ /// See [`Self::custom_tlvs`] for more info.
473
+ pub fn with_custom_tlvs ( mut self , mut custom_tlvs : Vec < ( u64 , Vec < u8 > ) > ) -> Result < Self , ( ) > {
474
+ custom_tlvs. sort_unstable_by_key ( |( typ, _) | * typ) ;
475
+ let mut prev_type = None ;
476
+ for ( typ, _) in custom_tlvs. iter ( ) {
477
+ if * typ < 1 << 16 { return Err ( ( ) ) ; }
478
+ if * typ == 5482373484 { return Err ( ( ) ) ; } // keysend
479
+ match prev_type {
480
+ Some ( prev) if prev >= * typ => return Err ( ( ) ) ,
481
+ _ => { } ,
482
+ }
483
+ prev_type = Some ( * typ) ;
484
+ }
485
+ self . custom_tlvs = custom_tlvs;
486
+ Ok ( self )
487
+ }
488
+
489
+ /// Gets the custom TLVs that will be sent or have been received.
490
+ ///
491
+ /// Custom TLVs allow sending extra application-specific data with a payment. They provide
492
+ /// additional flexibility on top of payment metadata, as while other implementations may
493
+ /// require `payment_metadata` to reflect metadata provided in an invoice, custom TLVs
494
+ /// do not have this restriction.
495
+ ///
496
+ /// Note that if this field is non-empty, it will contain strictly increasing TLVs, each
497
+ /// represented by a `(u64, Vec<u8>)` for its type number and serialized value respectively.
498
+ /// This is validated when setting this field using [`Self::with_custom_tlvs`].
499
+ pub fn custom_tlvs ( & self ) -> & Vec < ( u64 , Vec < u8 > ) > {
500
+ & self . custom_tlvs
459
501
}
460
502
461
503
/// When we have received some HTLC(s) towards an MPP payment, as we receive further HTLC(s) we
@@ -773,6 +815,7 @@ impl OutboundPayments {
773
815
( * total_msat, RecipientOnionFields {
774
816
payment_secret : * payment_secret,
775
817
payment_metadata : payment_metadata. clone ( ) ,
818
+ custom_tlvs : Vec :: new ( ) ,
776
819
} , * keysend_preimage)
777
820
} ,
778
821
PendingOutboundPayment :: Legacy { .. } => {
@@ -1450,6 +1493,28 @@ mod tests {
1450
1493
1451
1494
use alloc:: collections:: VecDeque ;
1452
1495
1496
+ #[ test]
1497
+ fn test_recipient_onion_fields_with_custom_tlvs ( ) {
1498
+ let onion_fields = RecipientOnionFields :: spontaneous_empty ( ) ;
1499
+
1500
+ let bad_type_range_tlvs = vec ! [
1501
+ ( 0 , vec![ 42 ] ) ,
1502
+ ( 1 , vec![ 42 ; 32 ] ) ,
1503
+ ] ;
1504
+ assert ! ( onion_fields. clone( ) . with_custom_tlvs( bad_type_range_tlvs) . is_err( ) ) ;
1505
+
1506
+ let keysend_tlv = vec ! [
1507
+ ( 5482373484 , vec![ 42 ; 32 ] ) ,
1508
+ ] ;
1509
+ assert ! ( onion_fields. clone( ) . with_custom_tlvs( keysend_tlv) . is_err( ) ) ;
1510
+
1511
+ let good_tlvs = vec ! [
1512
+ ( ( 1 << 16 ) + 1 , vec![ 42 ] ) ,
1513
+ ( ( 1 << 16 ) + 3 , vec![ 42 ; 32 ] ) ,
1514
+ ] ;
1515
+ assert ! ( onion_fields. with_custom_tlvs( good_tlvs) . is_ok( ) ) ;
1516
+ }
1517
+
1453
1518
#[ test]
1454
1519
#[ cfg( feature = "std" ) ]
1455
1520
fn fails_paying_after_expiration ( ) {
0 commit comments