Skip to content

Commit 5a537ca

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 c821bbe commit 5a537ca

File tree

2 files changed

+76
-5
lines changed

2 files changed

+76
-5
lines changed

lightning/src/ln/channelmanager.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,13 @@ 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+
custom_tlvs: Option<Vec<(u64, Vec<u8>)>>,
113114
},
114115
ReceiveKeysend {
115116
payment_preimage: PaymentPreimage,
116117
payment_metadata: Option<Vec<u8>>,
117118
incoming_cltv_expiry: u32, // Used to track when we should expire pending HTLCs that go unclaimed
119+
custom_tlvs: Option<Vec<(u64, Vec<u8>)>>,
118120
},
119121
}
120122

@@ -2354,6 +2356,7 @@ where
23542356
payment_metadata,
23552357
incoming_cltv_expiry: hop_data.outgoing_cltv_value,
23562358
phantom_shared_secret,
2359+
custom_tlvs,
23572360
}
23582361
} else if let Some(payment_preimage) = keysend_preimage {
23592362
// We need to check that the sender knows the keysend preimage before processing this
@@ -2374,6 +2377,7 @@ where
23742377
payment_preimage,
23752378
payment_metadata,
23762379
incoming_cltv_expiry: hop_data.outgoing_cltv_value,
2380+
custom_tlvs,
23772381
}
23782382
} else {
23792383
return Err(ReceiveError {
@@ -3485,15 +3489,15 @@ where
34853489
}
34863490
}) => {
34873491
let (cltv_expiry, onion_payload, payment_data, phantom_shared_secret, mut onion_fields) = match routing {
3488-
PendingHTLCRouting::Receive { payment_data, payment_metadata, incoming_cltv_expiry, phantom_shared_secret } => {
3492+
PendingHTLCRouting::Receive { payment_data, payment_metadata, incoming_cltv_expiry, phantom_shared_secret, custom_tlvs } => {
34893493
let _legacy_hop_data = Some(payment_data.clone());
34903494
let onion_fields = RecipientOnionFields { payment_secret: Some(payment_data.payment_secret),
3491-
payment_metadata, custom_tlvs: None };
3495+
payment_metadata, custom_tlvs };
34923496
(incoming_cltv_expiry, OnionPayload::Invoice { _legacy_hop_data },
34933497
Some(payment_data), phantom_shared_secret, onion_fields)
34943498
},
3495-
PendingHTLCRouting::ReceiveKeysend { payment_preimage, payment_metadata, incoming_cltv_expiry } => {
3496-
let onion_fields = RecipientOnionFields { payment_secret: None, payment_metadata, custom_tlvs: None };
3499+
PendingHTLCRouting::ReceiveKeysend { payment_preimage, payment_metadata, incoming_cltv_expiry, custom_tlvs } => {
3500+
let onion_fields = RecipientOnionFields { payment_secret: None, payment_metadata, custom_tlvs };
34973501
(incoming_cltv_expiry, OnionPayload::Spontaneous(payment_preimage),
34983502
None, None, onion_fields)
34993503
},
@@ -7055,11 +7059,13 @@ impl_writeable_tlv_based_enum!(PendingHTLCRouting,
70557059
(1, phantom_shared_secret, option),
70567060
(2, incoming_cltv_expiry, required),
70577061
(3, payment_metadata, option),
7062+
(5, custom_tlvs, option),
70587063
},
70597064
(2, ReceiveKeysend) => {
70607065
(0, payment_preimage, required),
70617066
(2, incoming_cltv_expiry, required),
70627067
(3, payment_metadata, option),
7068+
(5, custom_tlvs, option),
70637069
},
70647070
;);
70657071

lightning/src/ln/payment_tests.rs

+66-1
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};
2121
use crate::ln::features::InvoiceFeatures;
@@ -3011,6 +3011,71 @@ fn claim_from_closed_chan() {
30113011
do_claim_from_closed_chan(false);
30123012
}
30133013

3014+
#[test]
3015+
fn test_custom_tlvs() {
3016+
do_test_custom_tlvs(true);
3017+
do_test_custom_tlvs(false);
3018+
}
3019+
3020+
fn do_test_custom_tlvs(spontaneous: bool) {
3021+
let chanmon_cfgs = create_chanmon_cfgs(2);
3022+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
3023+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None; 2]);
3024+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
3025+
3026+
create_announced_chan_between_nodes(&nodes, 0, 1);
3027+
3028+
let amt_msat = 100_000;
3029+
let (mut route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(&nodes[0], &nodes[1], amt_msat);
3030+
let payment_id = PaymentId(our_payment_hash.0);
3031+
let custom_tlvs = vec![
3032+
(5482373483, vec![1, 2, 3, 4]),
3033+
(5482373487, vec![0x42u8; 16]),
3034+
];
3035+
let onion_fields = RecipientOnionFields {
3036+
payment_secret: if spontaneous { None } else { Some(our_payment_secret) },
3037+
payment_metadata: None,
3038+
custom_tlvs: Some(custom_tlvs.clone())
3039+
};
3040+
if spontaneous {
3041+
nodes[0].node.send_spontaneous_payment(&route, Some(our_payment_preimage), onion_fields, payment_id).unwrap();
3042+
} else {
3043+
nodes[0].node.send_payment_with_route(&route, our_payment_hash, onion_fields, payment_id).unwrap();
3044+
}
3045+
check_added_monitors(&nodes[0], 1);
3046+
3047+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
3048+
let ev = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events);
3049+
let mut payment_event = SendEvent::from_event(ev);
3050+
3051+
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
3052+
check_added_monitors!(&nodes[1], 0);
3053+
commitment_signed_dance!(nodes[1], nodes[0], payment_event.commitment_msg, false);
3054+
expect_pending_htlcs_forwardable!(nodes[1]);
3055+
3056+
let events = nodes[1].node.get_and_clear_pending_events();
3057+
assert_eq!(events.len(), 1);
3058+
match events[0] {
3059+
Event::PaymentClaimable { ref purpose, amount_msat, ref onion_fields, .. } => {
3060+
match &purpose {
3061+
PaymentPurpose::InvoicePayment { payment_secret, .. } => {
3062+
assert_eq!(our_payment_secret, *payment_secret);
3063+
assert_eq!(Some(*payment_secret), onion_fields.as_ref().unwrap().payment_secret);
3064+
},
3065+
PaymentPurpose::SpontaneousPayment(payment_preimage) => {
3066+
assert_eq!(our_payment_preimage, *payment_preimage);
3067+
},
3068+
}
3069+
assert_eq!(amount_msat, amt_msat);
3070+
assert_eq!(onion_fields.clone().unwrap().custom_tlvs.unwrap(), custom_tlvs);
3071+
},
3072+
_ => panic!("Unexpected event"),
3073+
}
3074+
3075+
claim_payment(&nodes[0], &[&nodes[1]], our_payment_preimage);
3076+
}
3077+
3078+
30143079
fn do_test_payment_metadata_consistency(do_reload: bool, do_modify: bool) {
30153080
// Check that a payment metadata received on one HTLC that doesn't match the one received on
30163081
// another results in the HTLC being rejected.

0 commit comments

Comments
 (0)