Skip to content

Commit 0a2dbdf

Browse files
committed
Handle receiving custom HTLC TLVs
This completes basic receiver-side support for custom TLVs and adds functional testing for sending and receiving.
1 parent f560320 commit 0a2dbdf

File tree

3 files changed

+180
-5
lines changed

3 files changed

+180
-5
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,17 @@ pub(super) enum PendingHTLCRouting {
110110
payment_metadata: Option<Vec<u8>>,
111111
incoming_cltv_expiry: u32, // Used to track when we should expire pending HTLCs that go unclaimed
112112
phantom_shared_secret: Option<[u8; 32]>,
113+
/// See [`RecipientOnionFields::custom_tlvs`] for more info.
114+
custom_tlvs: Vec<(u64, Vec<u8>)>,
113115
},
114116
ReceiveKeysend {
115117
/// This was added in 0.0.116 and will break deserialization on downgrades.
116118
payment_data: Option<msgs::FinalOnionHopData>,
117119
payment_preimage: PaymentPreimage,
118120
payment_metadata: Option<Vec<u8>>,
119121
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>)>,
120124
},
121125
}
122126

@@ -2748,13 +2752,15 @@ where
27482752
payment_preimage,
27492753
payment_metadata,
27502754
incoming_cltv_expiry: outgoing_cltv_value,
2755+
custom_tlvs,
27512756
}
27522757
} else if let Some(data) = payment_data {
27532758
PendingHTLCRouting::Receive {
27542759
payment_data: data,
27552760
payment_metadata,
27562761
incoming_cltv_expiry: outgoing_cltv_value,
27572762
phantom_shared_secret,
2763+
custom_tlvs,
27582764
}
27592765
} else {
27602766
return Err(InboundOnionErr {
@@ -3941,18 +3947,18 @@ where
39413947
}
39423948
}) => {
39433949
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 } => {
3950+
PendingHTLCRouting::Receive { payment_data, payment_metadata, incoming_cltv_expiry, phantom_shared_secret, custom_tlvs } => {
39453951
let _legacy_hop_data = Some(payment_data.clone());
39463952
let onion_fields = RecipientOnionFields { payment_secret: Some(payment_data.payment_secret),
3947-
payment_metadata, custom_tlvs: vec![] };
3953+
payment_metadata, custom_tlvs };
39483954
(incoming_cltv_expiry, OnionPayload::Invoice { _legacy_hop_data },
39493955
Some(payment_data), phantom_shared_secret, onion_fields)
39503956
},
3951-
PendingHTLCRouting::ReceiveKeysend { payment_data, payment_preimage, payment_metadata, incoming_cltv_expiry } => {
3957+
PendingHTLCRouting::ReceiveKeysend { payment_data, payment_preimage, payment_metadata, incoming_cltv_expiry, custom_tlvs } => {
39523958
let onion_fields = RecipientOnionFields {
39533959
payment_secret: payment_data.as_ref().map(|data| data.payment_secret),
39543960
payment_metadata,
3955-
custom_tlvs: vec![],
3961+
custom_tlvs,
39563962
};
39573963
(incoming_cltv_expiry, OnionPayload::Spontaneous(payment_preimage),
39583964
payment_data, None, onion_fields)
@@ -7639,12 +7645,14 @@ impl_writeable_tlv_based_enum!(PendingHTLCRouting,
76397645
(1, phantom_shared_secret, option),
76407646
(2, incoming_cltv_expiry, required),
76417647
(3, payment_metadata, option),
7648+
(5, custom_tlvs, optional_vec),
76427649
},
76437650
(2, ReceiveKeysend) => {
76447651
(0, payment_preimage, required),
76457652
(2, incoming_cltv_expiry, required),
76467653
(3, payment_metadata, option),
76477654
(4, payment_data, option), // Added in 0.0.116
7655+
(5, custom_tlvs, optional_vec),
76487656
},
76497657
;);
76507658

lightning/src/ln/payment_tests.rs

Lines changed: 165 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::chain::{ChannelMonitorUpdateStatus, Confirm, Listen, Watch};
1515
use crate::chain::channelmonitor::{ANTI_REORG_DELAY, HTLC_FAIL_BACK_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS};
1616
use crate::sign::EntropySource;
1717
use crate::chain::transaction::OutPoint;
18-
use crate::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentFailureReason};
18+
use crate::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentFailureReason, PaymentPurpose};
1919
use crate::ln::channel::EXPIRE_PREV_CONFIG_TICKS;
2020
use crate::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChannelManager, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PaymentSendFailure, IDEMPOTENCY_TIMEOUT_TICKS, RecentPaymentDetails, RecipientOnionFields, HTLCForwardInfo, PendingHTLCRouting, PendingAddHTLCInfo};
2121
use crate::ln::features::Bolt11InvoiceFeatures;
@@ -3385,6 +3385,170 @@ fn claim_from_closed_chan() {
33853385
do_claim_from_closed_chan(false);
33863386
}
33873387

3388+
#[test]
3389+
fn test_custom_tlvs() {
3390+
do_test_custom_tlvs(true);
3391+
do_test_custom_tlvs(false);
3392+
}
3393+
3394+
fn do_test_custom_tlvs(spontaneous: bool) {
3395+
let chanmon_cfgs = create_chanmon_cfgs(2);
3396+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
3397+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None; 2]);
3398+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
3399+
3400+
create_announced_chan_between_nodes(&nodes, 0, 1);
3401+
3402+
let amt_msat = 100_000;
3403+
let (mut route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(&nodes[0], &nodes[1], amt_msat);
3404+
let payment_id = PaymentId(our_payment_hash.0);
3405+
let custom_tlvs = vec![
3406+
(5482373483, vec![1, 2, 3, 4]),
3407+
(5482373487, vec![0x42u8; 16]),
3408+
];
3409+
let onion_fields = RecipientOnionFields {
3410+
payment_secret: if spontaneous { None } else { Some(our_payment_secret) },
3411+
payment_metadata: None,
3412+
custom_tlvs: custom_tlvs.clone()
3413+
};
3414+
if spontaneous {
3415+
nodes[0].node.send_spontaneous_payment(&route, Some(our_payment_preimage), onion_fields, payment_id).unwrap();
3416+
} else {
3417+
nodes[0].node.send_payment_with_route(&route, our_payment_hash, onion_fields, payment_id).unwrap();
3418+
}
3419+
check_added_monitors(&nodes[0], 1);
3420+
3421+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
3422+
let ev = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events);
3423+
let mut payment_event = SendEvent::from_event(ev);
3424+
3425+
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
3426+
check_added_monitors!(&nodes[1], 0);
3427+
commitment_signed_dance!(nodes[1], nodes[0], payment_event.commitment_msg, false);
3428+
expect_pending_htlcs_forwardable!(nodes[1]);
3429+
3430+
let events = nodes[1].node.get_and_clear_pending_events();
3431+
assert_eq!(events.len(), 1);
3432+
match events[0] {
3433+
Event::PaymentClaimable { ref purpose, amount_msat, ref onion_fields, .. } => {
3434+
match &purpose {
3435+
PaymentPurpose::InvoicePayment { payment_secret, .. } => {
3436+
assert_eq!(our_payment_secret, *payment_secret);
3437+
assert_eq!(Some(*payment_secret), onion_fields.as_ref().unwrap().payment_secret);
3438+
},
3439+
PaymentPurpose::SpontaneousPayment(payment_preimage) => {
3440+
assert_eq!(our_payment_preimage, *payment_preimage);
3441+
},
3442+
}
3443+
assert_eq!(amount_msat, amt_msat);
3444+
assert_eq!(onion_fields.clone().unwrap().custom_tlvs().clone(), custom_tlvs);
3445+
},
3446+
_ => panic!("Unexpected event"),
3447+
}
3448+
3449+
claim_payment(&nodes[0], &[&nodes[1]], our_payment_preimage);
3450+
}
3451+
3452+
#[test]
3453+
fn test_retry_custom_tlvs() {
3454+
// Test that custom TLVs are successfully sent on retries
3455+
let chanmon_cfgs = create_chanmon_cfgs(3);
3456+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
3457+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
3458+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
3459+
3460+
create_announced_chan_between_nodes(&nodes, 0, 1);
3461+
let (chan_2_update, _, chan_2_id, _) = create_announced_chan_between_nodes(&nodes, 2, 1);
3462+
3463+
// Rebalance
3464+
send_payment(&nodes[2], &vec!(&nodes[1])[..], 1_500_000);
3465+
3466+
let amt_msat = 1_000_000;
3467+
let (route, payment_hash, payment_preimage, payment_secret) =
3468+
get_route_and_payment_hash!(nodes[0], nodes[2], amt_msat);
3469+
3470+
// Initiate the payment
3471+
let payment_id = PaymentId(payment_hash.0);
3472+
let mut route_params = RouteParameters {
3473+
payment_params: route.payment_params.clone().unwrap(),
3474+
final_value_msat: amt_msat,
3475+
};
3476+
3477+
let custom_tlvs = vec![((1 << 16) + 1, vec![0x42u8; 16])];
3478+
let onion_fields = RecipientOnionFields::secret_only(payment_secret);
3479+
let onion_fields = onion_fields.with_custom_tlvs(custom_tlvs.clone()).unwrap();
3480+
3481+
nodes[0].router.expect_find_route(route_params.clone(), Ok(route.clone()));
3482+
nodes[0].node.send_payment(payment_hash, onion_fields,
3483+
payment_id, route_params.clone(), Retry::Attempts(1)).unwrap();
3484+
check_added_monitors!(nodes[0], 1); // one monitor per path
3485+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
3486+
assert_eq!(events.len(), 1);
3487+
3488+
// Add the HTLC along the first hop.
3489+
let fail_path_msgs_1 = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events);
3490+
let (update_add, commitment_signed) = match fail_path_msgs_1 {
3491+
MessageSendEvent::UpdateHTLCs { node_id: _, updates: msgs::CommitmentUpdate {
3492+
ref update_add_htlcs, ref update_fulfill_htlcs, ref update_fail_htlcs,
3493+
ref update_fail_malformed_htlcs, ref update_fee, ref commitment_signed }
3494+
} => {
3495+
assert_eq!(update_add_htlcs.len(), 1);
3496+
assert!(update_fail_htlcs.is_empty());
3497+
assert!(update_fulfill_htlcs.is_empty());
3498+
assert!(update_fail_malformed_htlcs.is_empty());
3499+
assert!(update_fee.is_none());
3500+
(update_add_htlcs[0].clone(), commitment_signed.clone())
3501+
},
3502+
_ => panic!("Unexpected event"),
3503+
};
3504+
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &update_add);
3505+
commitment_signed_dance!(nodes[1], nodes[0], commitment_signed, false);
3506+
3507+
// Attempt to forward the payment and complete the path's failure.
3508+
expect_pending_htlcs_forwardable!(&nodes[1]);
3509+
expect_pending_htlcs_forwardable_and_htlc_handling_failed!(&nodes[1],
3510+
vec![HTLCDestination::NextHopChannel {
3511+
node_id: Some(nodes[2].node.get_our_node_id()),
3512+
channel_id: chan_2_id
3513+
}]);
3514+
let htlc_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
3515+
assert!(htlc_updates.update_add_htlcs.is_empty());
3516+
assert_eq!(htlc_updates.update_fail_htlcs.len(), 1);
3517+
assert!(htlc_updates.update_fulfill_htlcs.is_empty());
3518+
assert!(htlc_updates.update_fail_malformed_htlcs.is_empty());
3519+
check_added_monitors!(nodes[1], 1);
3520+
nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(),
3521+
&htlc_updates.update_fail_htlcs[0]);
3522+
commitment_signed_dance!(nodes[0], nodes[1], htlc_updates.commitment_signed, false);
3523+
let mut events = nodes[0].node.get_and_clear_pending_events();
3524+
match events[1] {
3525+
Event::PendingHTLCsForwardable { .. } => {},
3526+
_ => panic!("Unexpected event")
3527+
}
3528+
events.remove(1);
3529+
expect_payment_failed_conditions_event(events, payment_hash, false,
3530+
PaymentFailedConditions::new().mpp_parts_remain());
3531+
3532+
// Rebalance the channel so the retry of the payment can succeed.
3533+
send_payment(&nodes[2], &vec!(&nodes[1])[..], 1_500_000);
3534+
3535+
// Retry the payment and make sure it succeeds
3536+
route_params.payment_params.previously_failed_channels.push(chan_2_update.contents.short_channel_id);
3537+
nodes[0].router.expect_find_route(route_params, Ok(route));
3538+
nodes[0].node.process_pending_htlc_forwards();
3539+
check_added_monitors!(nodes[0], 1);
3540+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
3541+
assert_eq!(events.len(), 1);
3542+
let payment_claimable = pass_along_path(&nodes[0], &[&nodes[1], &nodes[2]], 1_000_000,
3543+
payment_hash, Some(payment_secret), events.pop().unwrap(), true, None).unwrap();
3544+
let onion_fields = match payment_claimable {
3545+
Event::PaymentClaimable { onion_fields, .. } => onion_fields,
3546+
_ => panic!("Unexpected event"),
3547+
};
3548+
assert_eq!(onion_fields.unwrap().custom_tlvs(), &custom_tlvs);
3549+
claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], false, payment_preimage);
3550+
}
3551+
33883552
fn do_test_payment_metadata_consistency(do_reload: bool, do_modify: bool) {
33893553
// Check that a payment metadata received on one HTLC that doesn't match the one received on
33903554
// another results in the HTLC being rejected.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## Backwards Compatibility
2+
3+
* Since the addition of custom HTLC TLV support in 0.0.117, if you downgrade you may unintentionally accept payments with features you don't understand.

0 commit comments

Comments
 (0)