Skip to content

Commit fdbcbff

Browse files
committed
Enforce explicit claims on payments with even custom TLVs
Because we don't know which custom TLV type numbers the user is expecting (and it would be cumbersome for them to tell us), instead of failing unknown even custom TLVs on deserialization, we accept all custom TLVs, and pass them to the user to check whether they recognize them and choose to fail back if they don't. However, a user may not check for custom TLVs, in which case we should reject any even custom TLVs as unknown. This commit makes sure a user must explicitly accept a payment with even custom TLVs, by (1) making the default `ChannelManager::claim_funds` fail if the payment had even custom TLVs and (2) adding a new function `ChannelManager::claim_funds_with_known_custom_tlvs` that accepts them. This commit also refactors our custom TLVs test and updates various documentation to account for this.
1 parent 9e66ceb commit fdbcbff

File tree

4 files changed

+86
-9
lines changed

4 files changed

+86
-9
lines changed

lightning/src/events/mod.rs

+15-3
Original file line numberDiff line numberDiff line change
@@ -355,9 +355,19 @@ pub enum Event {
355355
/// Note that if the preimage is not known, you should call
356356
/// [`ChannelManager::fail_htlc_backwards`] or [`ChannelManager::fail_htlc_backwards_with_reason`]
357357
/// to free up resources for this HTLC and avoid network congestion.
358-
/// If you fail to call either [`ChannelManager::claim_funds`], [`ChannelManager::fail_htlc_backwards`],
359-
/// or [`ChannelManager::fail_htlc_backwards_with_reason`] within the HTLC's timeout, the HTLC will be
360-
/// automatically failed.
358+
///
359+
/// If [`Event::PaymentClaimable::onion_fields`] is `Some`, and includes custom TLVs with even type
360+
/// numbers, you should use [`ChannelManager::fail_htlc_backwards_with_reason`] with
361+
/// [`FailureCode::InvalidOnionPayload`] if you fail to decode these TLVs, or
362+
/// [`ChannelManager::claim_funds_with_known_custom_tlvs`] if you successfully decode them.
363+
/// If you don't intend to check for custom TLVs, you can simply use
364+
/// [`ChannelManager::claim_funds`], which will automatically fail back even custom TLVs.
365+
///
366+
/// If you fail to call [`ChannelManager::claim_funds`],
367+
/// [`ChannelManager::claim_funds_with_known_custom_tlvs`],
368+
/// [`ChannelManager::fail_htlc_backwards`], or
369+
/// [`ChannelManager::fail_htlc_backwards_with_reason`] within the HTLC's timeout, the HTLC will
370+
/// be automatically failed.
361371
///
362372
/// # Note
363373
/// LDK will not stop an inbound payment from being paid multiple times, so multiple
@@ -369,6 +379,8 @@ pub enum Event {
369379
/// This event used to be called `PaymentReceived` in LDK versions 0.0.112 and earlier.
370380
///
371381
/// [`ChannelManager::claim_funds`]: crate::ln::channelmanager::ChannelManager::claim_funds
382+
/// [`ChannelManager::claim_funds_with_known_custom_tlvs`]: crate::ln::channelmanager::ChannelManager::claim_funds_with_known_custom_tlvs
383+
/// [`FailureCode::InvalidOnionPayload`]: crate::ln::channelmanager::FailureCode::InvalidOnionPayload
372384
/// [`ChannelManager::fail_htlc_backwards`]: crate::ln::channelmanager::ChannelManager::fail_htlc_backwards
373385
/// [`ChannelManager::fail_htlc_backwards_with_reason`]: crate::ln::channelmanager::ChannelManager::fail_htlc_backwards_with_reason
374386
PaymentClaimable {

lightning/src/ln/channelmanager.rs

+39
Original file line numberDiff line numberDiff line change
@@ -4388,13 +4388,35 @@ where
43884388
/// event matches your expectation. If you fail to do so and call this method, you may provide
43894389
/// the sender "proof-of-payment" when they did not fulfill the full expected payment.
43904390
///
4391+
/// This function will fail the payment if it has custom TLVs with even type numbers, as we
4392+
/// will assume they are unknown. If you intend to accept even custom TLVs, you should use
4393+
/// [`claim_funds_with_known_custom_tlvs`].
4394+
///
43914395
/// [`Event::PaymentClaimable`]: crate::events::Event::PaymentClaimable
43924396
/// [`Event::PaymentClaimable::claim_deadline`]: crate::events::Event::PaymentClaimable::claim_deadline
43934397
/// [`Event::PaymentClaimed`]: crate::events::Event::PaymentClaimed
43944398
/// [`process_pending_events`]: EventsProvider::process_pending_events
43954399
/// [`create_inbound_payment`]: Self::create_inbound_payment
43964400
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
4401+
/// [`claim_funds_with_known_custom_tlvs`]: Self::claim_funds_with_known_custom_tlvs
43974402
pub fn claim_funds(&self, payment_preimage: PaymentPreimage) {
4403+
self.claim_payment_internal(payment_preimage, false);
4404+
}
4405+
4406+
/// This is a variant of [`claim_funds`] that allows accepting a payment with custom TLVs with
4407+
/// even type numbers.
4408+
///
4409+
/// # Note
4410+
///
4411+
/// You MUST check you've understood all even TLVs before using this to
4412+
/// claim, otherwise you may unintentionally agree to some protocol you do not understand.
4413+
///
4414+
/// [`claim_funds`]: Self::claim_funds
4415+
pub fn claim_funds_with_known_custom_tlvs(&self, payment_preimage: PaymentPreimage) {
4416+
self.claim_payment_internal(payment_preimage, true);
4417+
}
4418+
4419+
fn claim_payment_internal(&self, payment_preimage: PaymentPreimage, custom_tlvs_known: bool) {
43984420
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
43994421

44004422
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
@@ -4421,6 +4443,23 @@ where
44214443
log_error!(self.logger, "Got a duplicate pending claimable event on payment hash {}! Please report this bug",
44224444
log_bytes!(payment_hash.0));
44234445
}
4446+
4447+
if let Some(RecipientOnionFields { custom_tlvs: Some(ref tlvs), .. }) = payment.onion_fields {
4448+
if !custom_tlvs_known && tlvs.iter().any(|(typ, _)| typ % 2 == 0) {
4449+
log_info!(self.logger, "Cannot accept payment with unknown even TLVs. Rejecting payment with payment hash {}",
4450+
log_bytes!(payment_hash.0));
4451+
claimable_payments.pending_claiming_payments.remove(&payment_hash);
4452+
mem::drop(claimable_payments);
4453+
for htlc in payment.htlcs {
4454+
let reason = self.get_htlc_fail_reason_from_failure_code(FailureCode::InvalidOnionPayload(None), &htlc);
4455+
let source = HTLCSource::PreviousHopData(htlc.prev_hop);
4456+
let receiver = HTLCDestination::FailedPayment { payment_hash };
4457+
self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
4458+
}
4459+
return;
4460+
}
4461+
}
4462+
44244463
payment.htlcs
44254464
} else { return; }
44264465
};

lightning/src/ln/functional_test_utils.rs

+3
Original file line numberDiff line numberDiff line change
@@ -2169,7 +2169,10 @@ pub fn do_claim_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>,
21692169
assert_eq!(path.last().unwrap().node.get_our_node_id(), expected_paths[0].last().unwrap().node.get_our_node_id());
21702170
}
21712171
expected_paths[0].last().unwrap().node.claim_funds(our_payment_preimage);
2172+
pass_claimed_payment_along_route(origin_node, expected_paths, skip_last, our_payment_preimage)
2173+
}
21722174

2175+
pub fn pass_claimed_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_paths: &[&[&Node<'a, 'b, 'c>]], skip_last: bool, our_payment_preimage: PaymentPreimage) -> u64 {
21732176
let claim_event = expected_paths[0].last().unwrap().node.get_and_clear_pending_events();
21742177
assert_eq!(claim_event.len(), 1);
21752178
match claim_event[0] {

lightning/src/ln/payment_tests.rs

+29-6
Original file line numberDiff line numberDiff line change
@@ -3197,12 +3197,20 @@ fn claim_from_closed_chan() {
31973197
}
31983198

31993199
#[test]
3200-
fn test_custom_tlvs() {
3201-
do_test_custom_tlvs(true);
3202-
do_test_custom_tlvs(false);
3200+
fn test_custom_tlvs_basic() {
3201+
do_test_custom_tlvs(false, false, false);
3202+
do_test_custom_tlvs(true, false, false);
32033203
}
32043204

3205-
fn do_test_custom_tlvs(spontaneous: bool) {
3205+
#[test]
3206+
fn test_custom_tlvs_explicit_claim() {
3207+
// Test that when receiving even custom TLVs the user must explicitly accept in case they
3208+
// are unknown.
3209+
do_test_custom_tlvs(false, true, false);
3210+
do_test_custom_tlvs(false, true, true);
3211+
}
3212+
3213+
fn do_test_custom_tlvs(spontaneous: bool, even_tlvs: bool, known_tlvs: bool) {
32063214
let chanmon_cfgs = create_chanmon_cfgs(2);
32073215
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
32083216
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None; 2]);
@@ -3214,7 +3222,7 @@ fn do_test_custom_tlvs(spontaneous: bool) {
32143222
let (mut route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(&nodes[0], &nodes[1], amt_msat);
32153223
let payment_id = PaymentId(our_payment_hash.0);
32163224
let custom_tlvs = vec![
3217-
(5482373483, vec![1, 2, 3, 4]),
3225+
(if even_tlvs { 5482373482 } else { 5482373483 }, vec![1, 2, 3, 4]),
32183226
(5482373487, vec![0x42u8; 16]),
32193227
];
32203228
let onion_fields = RecipientOnionFields {
@@ -3257,7 +3265,22 @@ fn do_test_custom_tlvs(spontaneous: bool) {
32573265
_ => panic!("Unexpected event"),
32583266
}
32593267

3260-
claim_payment(&nodes[0], &[&nodes[1]], our_payment_preimage);
3268+
match (known_tlvs, even_tlvs) {
3269+
(true, _) => {
3270+
nodes[1].node.claim_funds_with_known_custom_tlvs(our_payment_preimage);
3271+
let expected_total_fee_msat = pass_claimed_payment_along_route(&nodes[0], &[&[&nodes[1]]], false, our_payment_preimage);
3272+
expect_payment_sent!(&nodes[0], our_payment_preimage, Some(expected_total_fee_msat));
3273+
},
3274+
(false, false) => {
3275+
claim_payment(&nodes[0], &[&nodes[1]], our_payment_preimage);
3276+
},
3277+
(false, true) => {
3278+
nodes[1].node.claim_funds(our_payment_preimage);
3279+
let expected_destinations = vec![HTLCDestination::FailedPayment { payment_hash: our_payment_hash }];
3280+
expect_pending_htlcs_forwardable_and_htlc_handling_failed!(nodes[1], expected_destinations);
3281+
pass_failed_payment_back(&nodes[0], &[&[&nodes[1]]], false, our_payment_hash, PaymentFailureReason::RecipientRejected);
3282+
}
3283+
}
32613284
}
32623285

32633286
#[test]

0 commit comments

Comments
 (0)