@@ -110,13 +110,17 @@ pub(super) enum PendingHTLCRouting {
110
110
payment_metadata: Option<Vec<u8>>,
111
111
incoming_cltv_expiry: u32, // Used to track when we should expire pending HTLCs that go unclaimed
112
112
phantom_shared_secret: Option<[u8; 32]>,
113
+ /// See [`RecipientOnionFields::custom_tlvs`] for more info.
114
+ custom_tlvs: Vec<(u64, Vec<u8>)>,
113
115
},
114
116
ReceiveKeysend {
115
117
/// This was added in 0.0.116 and will break deserialization on downgrades.
116
118
payment_data: Option<msgs::FinalOnionHopData>,
117
119
payment_preimage: PaymentPreimage,
118
120
payment_metadata: Option<Vec<u8>>,
119
121
incoming_cltv_expiry: u32, // Used to track when we should expire pending HTLCs that go unclaimed
122
+ /// See [`RecipientOnionFields::custom_tlvs`] for more info.
123
+ custom_tlvs: Vec<(u64, Vec<u8>)>,
120
124
},
121
125
}
122
126
@@ -355,15 +359,32 @@ struct InboundOnionErr {
355
359
pub enum FailureCode {
356
360
/// We had a temporary error processing the payment. Useful if no other error codes fit
357
361
/// and you want to indicate that the payer may want to retry.
358
- TemporaryNodeFailure = 0x2000 | 2 ,
362
+ TemporaryNodeFailure,
359
363
/// We have a required feature which was not in this onion. For example, you may require
360
364
/// some additional metadata that was not provided with this payment.
361
- RequiredNodeFeatureMissing = 0x4000 | 0x2000 | 3 ,
365
+ RequiredNodeFeatureMissing,
362
366
/// You may wish to use this when a `payment_preimage` is unknown, or the CLTV expiry of
363
367
/// the HTLC is too close to the current block height for safe handling.
364
368
/// Using this failure code in [`ChannelManager::fail_htlc_backwards_with_reason`] is
365
369
/// equivalent to calling [`ChannelManager::fail_htlc_backwards`].
366
- IncorrectOrUnknownPaymentDetails = 0x4000 | 15 ,
370
+ IncorrectOrUnknownPaymentDetails,
371
+ /// We failed to process the payload after the onion was decrypted. You may wish to
372
+ /// use this when receiving custom HTLC TLVs with even type numbers that you don't recognize.
373
+ ///
374
+ /// If available, the tuple data may include the type number and byte offset in the
375
+ /// decrypted byte stream where the failure occurred.
376
+ InvalidOnionPayload(Option<(u64, u16)>),
377
+ }
378
+
379
+ impl Into<u16> for FailureCode {
380
+ fn into(self) -> u16 {
381
+ match self {
382
+ FailureCode::TemporaryNodeFailure => 0x2000 | 2,
383
+ FailureCode::RequiredNodeFeatureMissing => 0x4000 | 0x2000 | 3,
384
+ FailureCode::IncorrectOrUnknownPaymentDetails => 0x4000 | 15,
385
+ FailureCode::InvalidOnionPayload(_) => 0x4000 | 22,
386
+ }
387
+ }
367
388
}
368
389
369
390
/// Error type returned across the peer_state mutex boundary. When an Err is generated for a
@@ -2674,11 +2695,11 @@ where
2674
2695
amt_msat: u64, cltv_expiry: u32, phantom_shared_secret: Option<[u8; 32]>, allow_underpay: bool,
2675
2696
counterparty_skimmed_fee_msat: Option<u64>,
2676
2697
) -> Result<PendingHTLCInfo, InboundOnionErr> {
2677
- let ( payment_data, keysend_preimage, onion_amt_msat, outgoing_cltv_value, payment_metadata) = match hop_data {
2698
+ let (payment_data, keysend_preimage, custom_tlvs, onion_amt_msat, outgoing_cltv_value, payment_metadata) = match hop_data {
2678
2699
msgs::InboundOnionPayload::Receive {
2679
- payment_data, keysend_preimage, amt_msat, outgoing_cltv_value, payment_metadata, ..
2700
+ payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata, ..
2680
2701
} =>
2681
- ( payment_data, keysend_preimage, amt_msat, outgoing_cltv_value, payment_metadata) ,
2702
+ (payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata),
2682
2703
_ =>
2683
2704
return Err(InboundOnionErr {
2684
2705
err_code: 0x4000|22,
@@ -2748,13 +2769,15 @@ where
2748
2769
payment_preimage,
2749
2770
payment_metadata,
2750
2771
incoming_cltv_expiry: outgoing_cltv_value,
2772
+ custom_tlvs,
2751
2773
}
2752
2774
} else if let Some(data) = payment_data {
2753
2775
PendingHTLCRouting::Receive {
2754
2776
payment_data: data,
2755
2777
payment_metadata,
2756
2778
incoming_cltv_expiry: outgoing_cltv_value,
2757
2779
phantom_shared_secret,
2780
+ custom_tlvs,
2758
2781
}
2759
2782
} else {
2760
2783
return Err(InboundOnionErr {
@@ -3941,17 +3964,18 @@ where
3941
3964
}
3942
3965
}) => {
3943
3966
let (cltv_expiry, onion_payload, payment_data, phantom_shared_secret, mut onion_fields) = match routing {
3944
- PendingHTLCRouting :: Receive { payment_data, payment_metadata, incoming_cltv_expiry, phantom_shared_secret } => {
3967
+ PendingHTLCRouting::Receive { payment_data, payment_metadata, incoming_cltv_expiry, phantom_shared_secret, custom_tlvs } => {
3945
3968
let _legacy_hop_data = Some(payment_data.clone());
3946
- let onion_fields =
3947
- RecipientOnionFields { payment_secret : Some ( payment_data . payment_secret ) , payment_metadata } ;
3969
+ let onion_fields = RecipientOnionFields { payment_secret: Some(payment_data.payment_secret),
3970
+ payment_metadata, custom_tlvs };
3948
3971
(incoming_cltv_expiry, OnionPayload::Invoice { _legacy_hop_data },
3949
3972
Some(payment_data), phantom_shared_secret, onion_fields)
3950
3973
},
3951
- PendingHTLCRouting :: ReceiveKeysend { payment_data, payment_preimage, payment_metadata, incoming_cltv_expiry } => {
3974
+ PendingHTLCRouting::ReceiveKeysend { payment_data, payment_preimage, payment_metadata, incoming_cltv_expiry, custom_tlvs } => {
3952
3975
let onion_fields = RecipientOnionFields {
3953
3976
payment_secret: payment_data.as_ref().map(|data| data.payment_secret),
3954
- payment_metadata
3977
+ payment_metadata,
3978
+ custom_tlvs,
3955
3979
};
3956
3980
(incoming_cltv_expiry, OnionPayload::Spontaneous(payment_preimage),
3957
3981
payment_data, None, onion_fields)
@@ -4576,12 +4600,19 @@ where
4576
4600
/// Gets error data to form an [`HTLCFailReason`] given a [`FailureCode`] and [`ClaimableHTLC`].
4577
4601
fn get_htlc_fail_reason_from_failure_code(&self, failure_code: FailureCode, htlc: &ClaimableHTLC) -> HTLCFailReason {
4578
4602
match failure_code {
4579
- FailureCode :: TemporaryNodeFailure => HTLCFailReason :: from_failure_code ( failure_code as u16 ) ,
4580
- FailureCode :: RequiredNodeFeatureMissing => HTLCFailReason :: from_failure_code ( failure_code as u16 ) ,
4603
+ FailureCode::TemporaryNodeFailure => HTLCFailReason::from_failure_code(failure_code.into() ),
4604
+ FailureCode::RequiredNodeFeatureMissing => HTLCFailReason::from_failure_code(failure_code.into() ),
4581
4605
FailureCode::IncorrectOrUnknownPaymentDetails => {
4582
4606
let mut htlc_msat_height_data = htlc.value.to_be_bytes().to_vec();
4583
4607
htlc_msat_height_data.extend_from_slice(&self.best_block.read().unwrap().height().to_be_bytes());
4584
- HTLCFailReason :: reason ( failure_code as u16 , htlc_msat_height_data)
4608
+ HTLCFailReason::reason(failure_code.into(), htlc_msat_height_data)
4609
+ },
4610
+ FailureCode::InvalidOnionPayload(data) => {
4611
+ let fail_data = match data {
4612
+ Some((typ, offset)) => [BigSize(typ).encode(), offset.encode()].concat(),
4613
+ None => Vec::new(),
4614
+ };
4615
+ HTLCFailReason::reason(failure_code.into(), fail_data)
4585
4616
}
4586
4617
}
4587
4618
}
@@ -4728,13 +4759,35 @@ where
4728
4759
/// event matches your expectation. If you fail to do so and call this method, you may provide
4729
4760
/// the sender "proof-of-payment" when they did not fulfill the full expected payment.
4730
4761
///
4762
+ /// This function will fail the payment if it has custom TLVs with even type numbers, as we
4763
+ /// will assume they are unknown. If you intend to accept even custom TLVs, you should use
4764
+ /// [`claim_funds_with_known_custom_tlvs`].
4765
+ ///
4731
4766
/// [`Event::PaymentClaimable`]: crate::events::Event::PaymentClaimable
4732
4767
/// [`Event::PaymentClaimable::claim_deadline`]: crate::events::Event::PaymentClaimable::claim_deadline
4733
4768
/// [`Event::PaymentClaimed`]: crate::events::Event::PaymentClaimed
4734
4769
/// [`process_pending_events`]: EventsProvider::process_pending_events
4735
4770
/// [`create_inbound_payment`]: Self::create_inbound_payment
4736
4771
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
4772
+ /// [`claim_funds_with_known_custom_tlvs`]: Self::claim_funds_with_known_custom_tlvs
4737
4773
pub fn claim_funds(&self, payment_preimage: PaymentPreimage) {
4774
+ self.claim_payment_internal(payment_preimage, false);
4775
+ }
4776
+
4777
+ /// This is a variant of [`claim_funds`] that allows accepting a payment with custom TLVs with
4778
+ /// even type numbers.
4779
+ ///
4780
+ /// # Note
4781
+ ///
4782
+ /// You MUST check you've understood all even TLVs before using this to
4783
+ /// claim, otherwise you may unintentionally agree to some protocol you do not understand.
4784
+ ///
4785
+ /// [`claim_funds`]: Self::claim_funds
4786
+ pub fn claim_funds_with_known_custom_tlvs(&self, payment_preimage: PaymentPreimage) {
4787
+ self.claim_payment_internal(payment_preimage, true);
4788
+ }
4789
+
4790
+ fn claim_payment_internal(&self, payment_preimage: PaymentPreimage, custom_tlvs_known: bool) {
4738
4791
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
4739
4792
4740
4793
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
@@ -4761,6 +4814,23 @@ where
4761
4814
log_error!(self.logger, "Got a duplicate pending claimable event on payment hash {}! Please report this bug",
4762
4815
log_bytes!(payment_hash.0));
4763
4816
}
4817
+
4818
+ if let Some(RecipientOnionFields { ref custom_tlvs, .. }) = payment.onion_fields {
4819
+ if !custom_tlvs_known && custom_tlvs.iter().any(|(typ, _)| typ % 2 == 0) {
4820
+ log_info!(self.logger, "Rejecting payment with payment hash {} as we cannot accept payment with unknown even TLVs: {}",
4821
+ log_bytes!(payment_hash.0), log_iter!(custom_tlvs.iter().map(|(typ, _)| typ).filter(|typ| *typ % 2 == 0)));
4822
+ claimable_payments.pending_claiming_payments.remove(&payment_hash);
4823
+ mem::drop(claimable_payments);
4824
+ for htlc in payment.htlcs {
4825
+ let reason = self.get_htlc_fail_reason_from_failure_code(FailureCode::InvalidOnionPayload(None), &htlc);
4826
+ let source = HTLCSource::PreviousHopData(htlc.prev_hop);
4827
+ let receiver = HTLCDestination::FailedPayment { payment_hash };
4828
+ self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
4829
+ }
4830
+ return;
4831
+ }
4832
+ }
4833
+
4764
4834
payment.htlcs
4765
4835
} else { return; }
4766
4836
};
@@ -7638,12 +7708,14 @@ impl_writeable_tlv_based_enum!(PendingHTLCRouting,
7638
7708
(1, phantom_shared_secret, option),
7639
7709
(2, incoming_cltv_expiry, required),
7640
7710
(3, payment_metadata, option),
7711
+ (5, custom_tlvs, optional_vec),
7641
7712
},
7642
7713
(2, ReceiveKeysend) => {
7643
7714
(0, payment_preimage, required),
7644
7715
(2, incoming_cltv_expiry, required),
7645
7716
(3, payment_metadata, option),
7646
7717
(4, payment_data, option), // Added in 0.0.116
7718
+ (5, custom_tlvs, optional_vec),
7647
7719
},
7648
7720
;);
7649
7721
@@ -8750,6 +8822,7 @@ where
8750
8822
payment_secret: None, // only used for retries, and we'll never retry on startup
8751
8823
payment_metadata: None, // only used for retries, and we'll never retry on startup
8752
8824
keysend_preimage: None, // only used for retries, and we'll never retry on startup
8825
+ custom_tlvs: Vec::new(), // only used for retries, and we'll never retry on startup
8753
8826
pending_amt_msat: path_amt,
8754
8827
pending_fee_msat: Some(path_fee),
8755
8828
total_msat: path_amt,
@@ -10092,6 +10165,7 @@ mod tests {
10092
10165
payment_data: Some(msgs::FinalOnionHopData {
10093
10166
payment_secret: PaymentSecret([0; 32]), total_msat: sender_intended_amt_msat,
10094
10167
}),
10168
+ custom_tlvs: Vec::new(),
10095
10169
};
10096
10170
// Check that if the amount we received + the penultimate hop extra fee is less than the sender
10097
10171
// intended amount, we fail the payment.
@@ -10111,6 +10185,7 @@ mod tests {
10111
10185
payment_data: Some(msgs::FinalOnionHopData {
10112
10186
payment_secret: PaymentSecret([0; 32]), total_msat: sender_intended_amt_msat,
10113
10187
}),
10188
+ custom_tlvs: Vec::new(),
10114
10189
};
10115
10190
assert!(node[0].node.construct_recv_pending_htlc_info(hop_data, [0; 32], PaymentHash([0; 32]),
10116
10191
sender_intended_amt_msat - extra_fee_msat, 42, None, true, Some(extra_fee_msat)).is_ok());
0 commit comments