@@ -431,10 +431,20 @@ 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
+ /// Custom TLVs allow sending extra application-specific data with a payment. They provide
435
+ /// additional flexibility on top of payment metadata, as while other implementations may
436
+ /// require payment metadata to reflect metadata provided in an invoice, custom TLVs
437
+ /// do not have this restriction.
438
+ ///
439
+ /// Note that the if this field is `Some`, it will contain properly ordered TLVs, each
440
+ /// represented by a `(u64, Vec<u8>)` for its type number and serialized value respectively.
441
+ /// This is validated when setting this field using [`Self::with_custom_tlvs`].
442
+ pub ( super ) custom_tlvs : Option < Vec < ( u64 , Vec < u8 > ) > > ,
434
443
}
435
444
436
445
impl_writeable_tlv_based ! ( RecipientOnionFields , {
437
446
( 0 , payment_secret, option) ,
447
+ ( 1 , custom_tlvs, option) ,
438
448
( 2 , payment_metadata, option) ,
439
449
} ) ;
440
450
@@ -443,7 +453,7 @@ impl RecipientOnionFields {
443
453
/// set of onion fields for today's BOLT11 invoices - most nodes require a [`PaymentSecret`]
444
454
/// but do not require or provide any further data.
445
455
pub fn secret_only ( payment_secret : PaymentSecret ) -> Self {
446
- Self { payment_secret : Some ( payment_secret) , payment_metadata : None }
456
+ Self { payment_secret : Some ( payment_secret) , payment_metadata : None , custom_tlvs : None }
447
457
}
448
458
449
459
/// Creates a new [`RecipientOnionFields`] with no fields. This generally does not create
@@ -452,7 +462,25 @@ impl RecipientOnionFields {
452
462
///
453
463
/// [`ChannelManager::send_spontaneous_payment`]: super::channelmanager::ChannelManager::send_spontaneous_payment
454
464
pub fn spontaneous_empty ( ) -> Self {
455
- Self { payment_secret : None , payment_metadata : None }
465
+ Self { payment_secret : None , payment_metadata : None , custom_tlvs : None }
466
+ }
467
+
468
+ /// Creates a new [`RecipientOnionFields`], setting `custom_tlvs`. Each TLV is provided as a
469
+ /// `(u64, Vec<u8>)` for the type number and serialized value respectively. TLV type numbers
470
+ /// must be unique, in increasing order, and within the range reserved for custom types, i.e.
471
+ /// >= 2^16, otherwise this method will return `Err(())`.
472
+ pub fn with_custom_tlvs ( mut self , custom_tlvs : Vec < ( u64 , Vec < u8 > ) > ) -> Result < Self , ( ) > {
473
+ let mut prev_type = None ;
474
+ for ( typ, _) in custom_tlvs. iter ( ) {
475
+ if * typ < 1 << 16 { return Err ( ( ) ) ; }
476
+ match prev_type {
477
+ Some ( prev) if prev >= * typ => return Err ( ( ) ) ,
478
+ _ => { } ,
479
+ }
480
+ prev_type = Some ( * typ) ;
481
+ }
482
+ self . custom_tlvs = Some ( custom_tlvs) ;
483
+ Ok ( self )
456
484
}
457
485
458
486
/// When we have received some HTLC(s) towards an MPP payment, as we receive further HTLC(s) we
@@ -765,6 +793,7 @@ impl OutboundPayments {
765
793
( * total_msat, RecipientOnionFields {
766
794
payment_secret : * payment_secret,
767
795
payment_metadata : payment_metadata. clone ( ) ,
796
+ custom_tlvs : None ,
768
797
} , * keysend_preimage)
769
798
} ,
770
799
PendingOutboundPayment :: Legacy { .. } => {
@@ -1444,6 +1473,23 @@ mod tests {
1444
1473
1445
1474
use alloc:: collections:: VecDeque ;
1446
1475
1476
+ #[ test]
1477
+ fn test_recipient_onion_fields_with_custom_tlvs ( ) {
1478
+ let onion_fields = RecipientOnionFields :: spontaneous_empty ( ) ;
1479
+
1480
+ let bad_type_range_tlvs = vec ! [
1481
+ ( 0 , vec![ 42 ] ) ,
1482
+ ( 1 , vec![ 42 ; 32 ] ) ,
1483
+ ] ;
1484
+ assert ! ( onion_fields. clone( ) . with_custom_tlvs( bad_type_range_tlvs) . is_err( ) ) ;
1485
+
1486
+ let good_tlvs = vec ! [
1487
+ ( ( 1 << 16 ) + 1 , vec![ 42 ] ) ,
1488
+ ( ( 1 << 16 ) + 3 , vec![ 42 ; 32 ] ) ,
1489
+ ] ;
1490
+ assert ! ( onion_fields. with_custom_tlvs( good_tlvs) . is_ok( ) ) ;
1491
+ }
1492
+
1447
1493
#[ test]
1448
1494
#[ cfg( feature = "std" ) ]
1449
1495
fn fails_paying_after_expiration ( ) {
0 commit comments