@@ -13,10 +13,12 @@ use bitcoin::hashes::Hash;
13
13
use bitcoin:: hashes:: sha256:: Hash as Sha256 ;
14
14
use bitcoin:: secp256k1:: { self , Secp256k1 , SecretKey } ;
15
15
16
+ use crate :: offers:: TlvStream ;
16
17
use crate :: sign:: { EntropySource , NodeSigner , Recipient } ;
17
18
use crate :: events:: { self , PaymentFailureReason } ;
18
19
use crate :: ln:: { PaymentHash , PaymentPreimage , PaymentSecret } ;
19
20
use crate :: ln:: channelmanager:: { ChannelDetails , EventCompletionAction , HTLCSource , IDEMPOTENCY_TIMEOUT_TICKS , PaymentId } ;
21
+ use crate :: ln:: msgs:: DecodeError ;
20
22
use crate :: ln:: onion_utils:: HTLCFailReason ;
21
23
use crate :: routing:: router:: { InFlightHtlcs , Path , PaymentParameters , Route , RouteParameters , Router } ;
22
24
use crate :: util:: errors:: APIError ;
@@ -431,10 +433,20 @@ pub struct RecipientOnionFields {
431
433
/// [`Self::payment_secret`] and while nearly all lightning senders support secrets, metadata
432
434
/// may not be supported as universally.
433
435
pub payment_metadata : Option < Vec < u8 > > ,
436
+ /// Custom TLVs allow sending extra application-specific data with a payment. They provide
437
+ /// additional flexibility on top of payment metadata, as while other implementations may
438
+ /// require payment metadata to reflect metadata provided in an invoice, custom TLVs
439
+ /// do not have this restriction.
440
+ ///
441
+ /// Note that the if this field is `Some`, it will contain a properly serialized TLV stream,
442
+ /// otherwise we will fail to serialize and send the payment. This can be easily done using
443
+ /// [`encode_tlv_stream`], and is validated with [`Self::with_custom_tlvs`].
444
+ pub ( super ) custom_tlvs : Option < Vec < u8 > > ,
434
445
}
435
446
436
447
impl_writeable_tlv_based ! ( RecipientOnionFields , {
437
448
( 0 , payment_secret, option) ,
449
+ ( 1 , custom_tlvs, option) ,
438
450
( 2 , payment_metadata, option) ,
439
451
} ) ;
440
452
@@ -443,7 +455,7 @@ impl RecipientOnionFields {
443
455
/// set of onion fields for today's BOLT11 invoices - most nodes require a [`PaymentSecret`]
444
456
/// but do not require or provide any further data.
445
457
pub fn secret_only ( payment_secret : PaymentSecret ) -> Self {
446
- Self { payment_secret : Some ( payment_secret) , payment_metadata : None }
458
+ Self { payment_secret : Some ( payment_secret) , payment_metadata : None , custom_tlvs : None }
447
459
}
448
460
449
461
/// Creates a new [`RecipientOnionFields`] with no fields. This generally does not create
@@ -452,7 +464,20 @@ impl RecipientOnionFields {
452
464
///
453
465
/// [`ChannelManager::send_spontaneous_payment`]: super::channelmanager::ChannelManager::send_spontaneous_payment
454
466
pub fn spontaneous_empty ( ) -> Self {
455
- Self { payment_secret : None , payment_metadata : None }
467
+ Self { payment_secret : None , payment_metadata : None , custom_tlvs : None }
468
+ }
469
+
470
+ /// Creates a new [`RecipientOnionFields`], settting `custom_tlvs` if the provided bytes can
471
+ /// deserialized properly into TLVs, otherwise returns the error faced while decoding. This
472
+ /// will also reject TLVs with type numbers outside the range reserved for custom types, i.e.
473
+ /// <2^16.
474
+ pub fn with_custom_tlvs ( mut self , custom_tlvs : Vec < u8 > ) -> Result < Self , DecodeError > {
475
+ let tlv_stream = TlvStream :: try_new ( & custom_tlvs) ?;
476
+ for tlv in tlv_stream. into_iter ( ) {
477
+ if tlv. get_type ( ) < 1 << 16 { return Err ( DecodeError :: InvalidValue ) ; }
478
+ }
479
+ self . custom_tlvs = Some ( custom_tlvs) ;
480
+ Ok ( self )
456
481
}
457
482
458
483
/// When we have received some HTLC(s) towards an MPP payment, as we receive further HTLC(s) we
@@ -765,6 +790,7 @@ impl OutboundPayments {
765
790
( * total_msat, RecipientOnionFields {
766
791
payment_secret : * payment_secret,
767
792
payment_metadata : payment_metadata. clone ( ) ,
793
+ custom_tlvs : None ,
768
794
} , * keysend_preimage)
769
795
} ,
770
796
PendingOutboundPayment :: Legacy { .. } => {
@@ -1444,6 +1470,26 @@ mod tests {
1444
1470
1445
1471
use alloc:: collections:: VecDeque ;
1446
1472
1473
+ #[ test]
1474
+ fn test_recipient_onion_fields_with_custom_tlvs ( ) {
1475
+ let onion_fields = RecipientOnionFields :: spontaneous_empty ( ) ;
1476
+
1477
+ let bad_tlvs = vec ! [ 42 ; 32 ] ;
1478
+ assert ! ( onion_fields. clone( ) . with_custom_tlvs( bad_tlvs) . is_err( ) ) ;
1479
+
1480
+ let bad_type_range_tlvs = _get_encoded_tlv_stream ! ( {
1481
+ ( 0 , 42 , required) ,
1482
+ ( 1 , Some ( vec![ 42 ; 32 ] ) , option) ,
1483
+ } ) ;
1484
+ assert ! ( onion_fields. clone( ) . with_custom_tlvs( bad_type_range_tlvs) . is_err( ) ) ;
1485
+
1486
+ let good_tlvs = _get_encoded_tlv_stream ! ( {
1487
+ ( ( 1 << 16 ) + 1 , 42 , required) ,
1488
+ ( ( 1 << 16 ) + 3 , Some ( vec![ 42 ; 32 ] ) , option) ,
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