Skip to content

Commit 764458a

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 8bc2e85 commit 764458a

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
@@ -5160,15 +5160,42 @@ impl<
51605160
fn can_forward_htlc_should_intercept(
51615161
&self, msg: &msgs::UpdateAddHTLC, prev_chan_public: bool, next_hop: &NextPacketDetails,
51625162
) -> Result<bool, LocalHTLCFailureReason> {
5163+
let cur_height = self.best_block.read().unwrap().height + 1;
51635164
let outgoing_scid = match next_hop.outgoing_connector {
51645165
HopConnector::ShortChannelId(scid) => scid,
51655166
HopConnector::Dummy => {
51665167
// Dummy hops are only used for path padding and must not reach HTLC processing.
51675168
debug_assert!(false, "Dummy hop reached HTLC handling.");
51685169
return Err(LocalHTLCFailureReason::InvalidOnionPayload);
51695170
},
5171+
// We can't make forwarding checks on trampoline forwards where we don't know the
5172+
// outgoing channel on receipt of the incoming htlc. Our trampoline logic will check
5173+
// our required delta and fee later on, so here we just check that the forwarding node
5174+
// did not "skim" off some of the sender's intended fee/cltv.
51705175
HopConnector::Trampoline(_) => {
5171-
return Err(LocalHTLCFailureReason::InvalidTrampolineForward);
5176+
// We do not yet support reloading our trampoline HTLCs on restart, so we just
5177+
// fail them for now (except in tests).
5178+
#[cfg(not(test))]
5179+
{
5180+
return Err(LocalHTLCFailureReason::InvalidTrampolineForward);
5181+
}
5182+
5183+
#[cfg(test)]
5184+
{
5185+
if msg.amount_msat < next_hop.outgoing_amt_msat {
5186+
return Err(LocalHTLCFailureReason::FeeInsufficient);
5187+
}
5188+
5189+
check_incoming_htlc_cltv(
5190+
cur_height,
5191+
next_hop.outgoing_cltv_value,
5192+
msg.cltv_expiry,
5193+
0,
5194+
)?;
5195+
5196+
// TODO: add interception flag specifically for trampoline
5197+
return Ok(false);
5198+
}
51725199
},
51735200
};
51745201
// TODO: We do the fake SCID namespace check a bunch of times here (and indirectly via
@@ -5207,7 +5234,6 @@ impl<
52075234
},
52085235
};
52095236

5210-
let cur_height = self.best_block.read().unwrap().height + 1;
52115237
check_incoming_htlc_cltv(
52125238
cur_height,
52135239
next_hop.outgoing_cltv_value,

0 commit comments

Comments
 (0)