Skip to content

Commit e2668b9

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 b54f20d commit e2668b9

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

@@ -2707,13 +2711,15 @@ where
27072711
payment_preimage,
27082712
payment_metadata,
27092713
incoming_cltv_expiry: hop_data.outgoing_cltv_value,
2714+
custom_tlvs,
27102715
}
27112716
} else if let Some(data) = payment_data {
27122717
PendingHTLCRouting::Receive {
27132718
payment_data: data,
27142719
payment_metadata,
27152720
incoming_cltv_expiry: hop_data.outgoing_cltv_value,
27162721
phantom_shared_secret,
2722+
custom_tlvs,
27172723
}
27182724
} else {
27192725
return Err(ReceiveError {
@@ -3926,18 +3932,18 @@ where
39263932
}
39273933
}) => {
39283934
let (cltv_expiry, onion_payload, payment_data, phantom_shared_secret, mut onion_fields) = match routing {
3929-
PendingHTLCRouting::Receive { payment_data, payment_metadata, incoming_cltv_expiry, phantom_shared_secret } => {
3935+
PendingHTLCRouting::Receive { payment_data, payment_metadata, incoming_cltv_expiry, phantom_shared_secret, custom_tlvs } => {
39303936
let _legacy_hop_data = Some(payment_data.clone());
39313937
let onion_fields = RecipientOnionFields { payment_secret: Some(payment_data.payment_secret),
3932-
payment_metadata, custom_tlvs: vec![] };
3938+
payment_metadata, custom_tlvs };
39333939
(incoming_cltv_expiry, OnionPayload::Invoice { _legacy_hop_data },
39343940
Some(payment_data), phantom_shared_secret, onion_fields)
39353941
},
3936-
PendingHTLCRouting::ReceiveKeysend { payment_data, payment_preimage, payment_metadata, incoming_cltv_expiry } => {
3942+
PendingHTLCRouting::ReceiveKeysend { payment_data, payment_preimage, payment_metadata, incoming_cltv_expiry, custom_tlvs } => {
39373943
let onion_fields = RecipientOnionFields {
39383944
payment_secret: payment_data.as_ref().map(|data| data.payment_secret),
39393945
payment_metadata,
3940-
custom_tlvs: vec![],
3946+
custom_tlvs,
39413947
};
39423948
(incoming_cltv_expiry, OnionPayload::Spontaneous(payment_preimage),
39433949
payment_data, None, onion_fields)
@@ -7624,12 +7630,14 @@ impl_writeable_tlv_based_enum!(PendingHTLCRouting,
76247630
(1, phantom_shared_secret, option),
76257631
(2, incoming_cltv_expiry, required),
76267632
(3, payment_metadata, option),
7633+
(5, custom_tlvs, optional_vec),
76277634
},
76287635
(2, ReceiveKeysend) => {
76297636
(0, payment_preimage, required),
76307637
(2, incoming_cltv_expiry, required),
76317638
(3, payment_metadata, option),
76327639
(4, payment_data, option), // Added in 0.0.116
7640+
(5, custom_tlvs, optional_vec),
76337641
},
76347642
;);
76357643

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;
@@ -3327,6 +3327,170 @@ fn claim_from_closed_chan() {
33273327
do_claim_from_closed_chan(false);
33283328
}
33293329

3330+
#[test]
3331+
fn test_custom_tlvs() {
3332+
do_test_custom_tlvs(true);
3333+
do_test_custom_tlvs(false);
3334+
}
3335+
3336+
fn do_test_custom_tlvs(spontaneous: bool) {
3337+
let chanmon_cfgs = create_chanmon_cfgs(2);
3338+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
3339+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None; 2]);
3340+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
3341+
3342+
create_announced_chan_between_nodes(&nodes, 0, 1);
3343+
3344+
let amt_msat = 100_000;
3345+
let (mut route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(&nodes[0], &nodes[1], amt_msat);
3346+
let payment_id = PaymentId(our_payment_hash.0);
3347+
let custom_tlvs = vec![
3348+
(5482373483, vec![1, 2, 3, 4]),
3349+
(5482373487, vec![0x42u8; 16]),
3350+
];
3351+
let onion_fields = RecipientOnionFields {
3352+
payment_secret: if spontaneous { None } else { Some(our_payment_secret) },
3353+
payment_metadata: None,
3354+
custom_tlvs: custom_tlvs.clone()
3355+
};
3356+
if spontaneous {
3357+
nodes[0].node.send_spontaneous_payment(&route, Some(our_payment_preimage), onion_fields, payment_id).unwrap();
3358+
} else {
3359+
nodes[0].node.send_payment_with_route(&route, our_payment_hash, onion_fields, payment_id).unwrap();
3360+
}
3361+
check_added_monitors(&nodes[0], 1);
3362+
3363+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
3364+
let ev = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events);
3365+
let mut payment_event = SendEvent::from_event(ev);
3366+
3367+
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
3368+
check_added_monitors!(&nodes[1], 0);
3369+
commitment_signed_dance!(nodes[1], nodes[0], payment_event.commitment_msg, false);
3370+
expect_pending_htlcs_forwardable!(nodes[1]);
3371+
3372+
let events = nodes[1].node.get_and_clear_pending_events();
3373+
assert_eq!(events.len(), 1);
3374+
match events[0] {
3375+
Event::PaymentClaimable { ref purpose, amount_msat, ref onion_fields, .. } => {
3376+
match &purpose {
3377+
PaymentPurpose::InvoicePayment { payment_secret, .. } => {
3378+
assert_eq!(our_payment_secret, *payment_secret);
3379+
assert_eq!(Some(*payment_secret), onion_fields.as_ref().unwrap().payment_secret);
3380+
},
3381+
PaymentPurpose::SpontaneousPayment(payment_preimage) => {
3382+
assert_eq!(our_payment_preimage, *payment_preimage);
3383+
},
3384+
}
3385+
assert_eq!(amount_msat, amt_msat);
3386+
assert_eq!(onion_fields.clone().unwrap().custom_tlvs().clone(), custom_tlvs);
3387+
},
3388+
_ => panic!("Unexpected event"),
3389+
}
3390+
3391+
claim_payment(&nodes[0], &[&nodes[1]], our_payment_preimage);
3392+
}
3393+
3394+
#[test]
3395+
fn test_retry_custom_tlvs() {
3396+
// Test that custom TLVs are successfully sent on retries
3397+
let chanmon_cfgs = create_chanmon_cfgs(3);
3398+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
3399+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
3400+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
3401+
3402+
create_announced_chan_between_nodes(&nodes, 0, 1);
3403+
let (chan_2_update, _, chan_2_id, _) = create_announced_chan_between_nodes(&nodes, 2, 1);
3404+
3405+
// Rebalance
3406+
send_payment(&nodes[2], &vec!(&nodes[1])[..], 1_500_000);
3407+
3408+
let amt_msat = 1_000_000;
3409+
let (route, payment_hash, payment_preimage, payment_secret) =
3410+
get_route_and_payment_hash!(nodes[0], nodes[2], amt_msat);
3411+
3412+
// Initiate the payment
3413+
let payment_id = PaymentId(payment_hash.0);
3414+
let mut route_params = RouteParameters {
3415+
payment_params: route.payment_params.clone().unwrap(),
3416+
final_value_msat: amt_msat,
3417+
};
3418+
3419+
let custom_tlvs = vec![((1 << 16) + 1, vec![0x42u8; 16])];
3420+
let onion_fields = RecipientOnionFields::secret_only(payment_secret);
3421+
let onion_fields = onion_fields.with_custom_tlvs(custom_tlvs.clone()).unwrap();
3422+
3423+
nodes[0].router.expect_find_route(route_params.clone(), Ok(route.clone()));
3424+
nodes[0].node.send_payment(payment_hash, onion_fields,
3425+
payment_id, route_params.clone(), Retry::Attempts(1)).unwrap();
3426+
check_added_monitors!(nodes[0], 1); // one monitor per path
3427+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
3428+
assert_eq!(events.len(), 1);
3429+
3430+
// Add the HTLC along the first hop.
3431+
let fail_path_msgs_1 = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events);
3432+
let (update_add, commitment_signed) = match fail_path_msgs_1 {
3433+
MessageSendEvent::UpdateHTLCs { node_id: _, updates: msgs::CommitmentUpdate {
3434+
ref update_add_htlcs, ref update_fulfill_htlcs, ref update_fail_htlcs,
3435+
ref update_fail_malformed_htlcs, ref update_fee, ref commitment_signed }
3436+
} => {
3437+
assert_eq!(update_add_htlcs.len(), 1);
3438+
assert!(update_fail_htlcs.is_empty());
3439+
assert!(update_fulfill_htlcs.is_empty());
3440+
assert!(update_fail_malformed_htlcs.is_empty());
3441+
assert!(update_fee.is_none());
3442+
(update_add_htlcs[0].clone(), commitment_signed.clone())
3443+
},
3444+
_ => panic!("Unexpected event"),
3445+
};
3446+
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &update_add);
3447+
commitment_signed_dance!(nodes[1], nodes[0], commitment_signed, false);
3448+
3449+
// Attempt to forward the payment and complete the path's failure.
3450+
expect_pending_htlcs_forwardable!(&nodes[1]);
3451+
expect_pending_htlcs_forwardable_and_htlc_handling_failed!(&nodes[1],
3452+
vec![HTLCDestination::NextHopChannel {
3453+
node_id: Some(nodes[2].node.get_our_node_id()),
3454+
channel_id: chan_2_id
3455+
}]);
3456+
let htlc_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
3457+
assert!(htlc_updates.update_add_htlcs.is_empty());
3458+
assert_eq!(htlc_updates.update_fail_htlcs.len(), 1);
3459+
assert!(htlc_updates.update_fulfill_htlcs.is_empty());
3460+
assert!(htlc_updates.update_fail_malformed_htlcs.is_empty());
3461+
check_added_monitors!(nodes[1], 1);
3462+
nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(),
3463+
&htlc_updates.update_fail_htlcs[0]);
3464+
commitment_signed_dance!(nodes[0], nodes[1], htlc_updates.commitment_signed, false);
3465+
let mut events = nodes[0].node.get_and_clear_pending_events();
3466+
match events[1] {
3467+
Event::PendingHTLCsForwardable { .. } => {},
3468+
_ => panic!("Unexpected event")
3469+
}
3470+
events.remove(1);
3471+
expect_payment_failed_conditions_event(events, payment_hash, false,
3472+
PaymentFailedConditions::new().mpp_parts_remain());
3473+
3474+
// Rebalance the channel so the retry of the payment can succeed.
3475+
send_payment(&nodes[2], &vec!(&nodes[1])[..], 1_500_000);
3476+
3477+
// Retry the payment and make sure it succeeds
3478+
route_params.payment_params.previously_failed_channels.push(chan_2_update.contents.short_channel_id);
3479+
nodes[0].router.expect_find_route(route_params, Ok(route));
3480+
nodes[0].node.process_pending_htlc_forwards();
3481+
check_added_monitors!(nodes[0], 1);
3482+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
3483+
assert_eq!(events.len(), 1);
3484+
let payment_claimable = pass_along_path(&nodes[0], &[&nodes[1], &nodes[2]], 1_000_000,
3485+
payment_hash, Some(payment_secret), events.pop().unwrap(), true, None).unwrap();
3486+
let onion_fields = match payment_claimable {
3487+
Event::PaymentClaimable { onion_fields, .. } => onion_fields,
3488+
_ => panic!("Unexpected event"),
3489+
};
3490+
assert_eq!(onion_fields.unwrap().custom_tlvs(), &custom_tlvs);
3491+
claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], false, payment_preimage);
3492+
}
3493+
33303494
fn do_test_payment_metadata_consistency(do_reload: bool, do_modify: bool) {
33313495
// Check that a payment metadata received on one HTLC that doesn't match the one received on
33323496
// 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)