Skip to content

Commit 5c5df21

Browse files
committed
ln: process added trampoline htlcs with CLTV validation in tests
We can't perform proper validation because we don't know the outgoing channel id until we forward the HTLC, so we just perform a basic CLTV check. We don't yet have proper handling of trampoline forwards on restart, so we only enable this in our tests.
1 parent 99d0eed commit 5c5df21

2 files changed

Lines changed: 28 additions & 122 deletions

File tree

lightning/src/ln/blinded_payment_tests.rs

Lines changed: 0 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -2723,123 +2723,3 @@ fn do_test_trampoline_relay(blinded: bool, test_case: TrampolineTestCase) {
27232723
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage);
27242724
}
27252725
}
2726-
2727-
#[test]
2728-
#[rustfmt::skip]
2729-
fn test_trampoline_forward_rejection() {
2730-
const TOTAL_NODE_COUNT: usize = 3;
2731-
2732-
let chanmon_cfgs = create_chanmon_cfgs(TOTAL_NODE_COUNT);
2733-
let node_cfgs = create_node_cfgs(TOTAL_NODE_COUNT, &chanmon_cfgs);
2734-
let node_chanmgrs = create_node_chanmgrs(TOTAL_NODE_COUNT, &node_cfgs, &vec![None; TOTAL_NODE_COUNT]);
2735-
let mut nodes = create_network(TOTAL_NODE_COUNT, &node_cfgs, &node_chanmgrs);
2736-
2737-
let (_, _, chan_id_alice_bob, _) = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
2738-
let (_, _, chan_id_bob_carol, _) = create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0);
2739-
2740-
for i in 0..TOTAL_NODE_COUNT { // connect all nodes' blocks
2741-
connect_blocks(&nodes[i], (TOTAL_NODE_COUNT as u32) * CHAN_CONFIRM_DEPTH + 1 - nodes[i].best_block_info().1);
2742-
}
2743-
2744-
let alice_node_id = nodes[0].node().get_our_node_id();
2745-
let bob_node_id = nodes[1].node().get_our_node_id();
2746-
let carol_node_id = nodes[2].node().get_our_node_id();
2747-
2748-
let alice_bob_scid = nodes[0].node().list_channels().iter().find(|c| c.channel_id == chan_id_alice_bob).unwrap().short_channel_id.unwrap();
2749-
let bob_carol_scid = nodes[1].node().list_channels().iter().find(|c| c.channel_id == chan_id_bob_carol).unwrap().short_channel_id.unwrap();
2750-
2751-
let amt_msat = 1000;
2752-
let (payment_preimage, payment_hash, _) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), None);
2753-
2754-
let route = Route {
2755-
paths: vec![Path {
2756-
hops: vec![
2757-
// Bob
2758-
RouteHop {
2759-
pubkey: bob_node_id,
2760-
node_features: NodeFeatures::empty(),
2761-
short_channel_id: alice_bob_scid,
2762-
channel_features: ChannelFeatures::empty(),
2763-
fee_msat: 1000,
2764-
cltv_expiry_delta: 48,
2765-
maybe_announced_channel: false,
2766-
},
2767-
2768-
// Carol
2769-
RouteHop {
2770-
pubkey: carol_node_id,
2771-
node_features: NodeFeatures::empty(),
2772-
short_channel_id: bob_carol_scid,
2773-
channel_features: ChannelFeatures::empty(),
2774-
fee_msat: 0,
2775-
cltv_expiry_delta: 24 + 24 + 39,
2776-
maybe_announced_channel: false,
2777-
}
2778-
],
2779-
blinded_tail: Some(BlindedTail {
2780-
trampoline_hops: vec![
2781-
// Carol
2782-
TrampolineHop {
2783-
pubkey: carol_node_id,
2784-
node_features: Features::empty(),
2785-
fee_msat: amt_msat,
2786-
cltv_expiry_delta: 24,
2787-
},
2788-
2789-
// Alice (unreachable)
2790-
TrampolineHop {
2791-
pubkey: alice_node_id,
2792-
node_features: Features::empty(),
2793-
fee_msat: amt_msat,
2794-
cltv_expiry_delta: 24 + 39,
2795-
},
2796-
],
2797-
hops: vec![BlindedHop{
2798-
// Fake public key
2799-
blinded_node_id: alice_node_id,
2800-
encrypted_payload: vec![],
2801-
}],
2802-
blinding_point: alice_node_id,
2803-
excess_final_cltv_expiry_delta: 39,
2804-
final_value_msat: amt_msat,
2805-
})
2806-
}],
2807-
route_params: None,
2808-
};
2809-
2810-
nodes[0].node.send_payment_with_route(route.clone(), payment_hash, RecipientOnionFields::spontaneous_empty(amt_msat), PaymentId(payment_hash.0)).unwrap();
2811-
2812-
check_added_monitors(&nodes[0], 1);
2813-
2814-
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
2815-
assert_eq!(events.len(), 1);
2816-
let first_message_event = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events);
2817-
2818-
let route: &[&Node] = &[&nodes[1], &nodes[2]];
2819-
let args = PassAlongPathArgs::new(&nodes[0], route, amt_msat, payment_hash, first_message_event)
2820-
.with_payment_preimage(payment_preimage)
2821-
.without_claimable_event()
2822-
.expect_failure(HTLCHandlingFailureType::Receive { payment_hash });
2823-
do_pass_along_path(args);
2824-
2825-
{
2826-
let unblinded_node_updates = get_htlc_update_msgs(&nodes[2], &nodes[1].node.get_our_node_id());
2827-
nodes[1].node.handle_update_fail_htlc(
2828-
nodes[2].node.get_our_node_id(), &unblinded_node_updates.update_fail_htlcs[0]
2829-
);
2830-
do_commitment_signed_dance(&nodes[1], &nodes[2], &unblinded_node_updates.commitment_signed, true, false);
2831-
}
2832-
{
2833-
let unblinded_node_updates = get_htlc_update_msgs(&nodes[1], &nodes[0].node.get_our_node_id());
2834-
nodes[0].node.handle_update_fail_htlc(
2835-
nodes[1].node.get_our_node_id(), &unblinded_node_updates.update_fail_htlcs[0]
2836-
);
2837-
do_commitment_signed_dance(&nodes[0], &nodes[1], &unblinded_node_updates.commitment_signed, false, false);
2838-
}
2839-
{
2840-
// Expect UnknownNextPeer error while we are unable to route forwarding Trampoline payments.
2841-
let payment_failed_conditions = PaymentFailedConditions::new()
2842-
.expected_htlc_error_data(LocalHTLCFailureReason::UnknownNextPeer, &[0; 0]);
2843-
expect_payment_failed_conditions(&nodes[0], payment_hash, false, payment_failed_conditions);
2844-
}
2845-
}

lightning/src/ln/channelmanager.rs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5173,15 +5173,42 @@ impl<
51735173
fn can_forward_htlc_should_intercept(
51745174
&self, msg: &msgs::UpdateAddHTLC, prev_chan_public: bool, next_hop: &NextPacketDetails,
51755175
) -> Result<bool, LocalHTLCFailureReason> {
5176+
let cur_height = self.best_block.read().unwrap().height + 1;
51765177
let outgoing_scid = match next_hop.outgoing_connector {
51775178
HopConnector::ShortChannelId(scid) => scid,
51785179
HopConnector::Dummy => {
51795180
// Dummy hops are only used for path padding and must not reach HTLC processing.
51805181
debug_assert!(false, "Dummy hop reached HTLC handling.");
51815182
return Err(LocalHTLCFailureReason::InvalidOnionPayload);
51825183
},
5184+
// We can't make forwarding checks on trampoline forwards where we don't know the
5185+
// outgoing channel on receipt of the incoming htlc. Our trampoline logic will check
5186+
// our required delta and fee later on, so here we just check that the forwarding node
5187+
// did not "skim" off some of the sender's intended fee/cltv.
51835188
HopConnector::Trampoline(_) => {
5184-
return Err(LocalHTLCFailureReason::InvalidTrampolineForward);
5189+
// We do not yet support reloading our trampoline HTLCs on restart, so we just
5190+
// fail them for now (except in tests).
5191+
#[cfg(not(test))]
5192+
{
5193+
return Err(LocalHTLCFailureReason::InvalidTrampolineForward);
5194+
}
5195+
5196+
#[cfg(test)]
5197+
{
5198+
if msg.amount_msat < next_hop.outgoing_amt_msat {
5199+
return Err(LocalHTLCFailureReason::FeeInsufficient);
5200+
}
5201+
5202+
check_incoming_htlc_cltv(
5203+
cur_height,
5204+
next_hop.outgoing_cltv_value,
5205+
msg.cltv_expiry,
5206+
0,
5207+
)?;
5208+
5209+
// TODO: add interception flag specifically for trampoline
5210+
return Ok(false);
5211+
}
51855212
},
51865213
};
51875214
// TODO: We do the fake SCID namespace check a bunch of times here (and indirectly via
@@ -5220,7 +5247,6 @@ impl<
52205247
},
52215248
};
52225249

5223-
let cur_height = self.best_block.read().unwrap().height + 1;
52245250
check_incoming_htlc_cltv(
52255251
cur_height,
52265252
next_hop.outgoing_cltv_value,

0 commit comments

Comments
 (0)