Skip to content

Commit

Permalink
Use temporary_node_failure for a phantom HTLC with bogus CLTV
Browse files Browse the repository at this point in the history
When we receive a phantom HTLC with a bogus/modified CLTV, we
should fail back with `incorrect_cltv_expiry`, but that requires a
`channel_update`, which we cannot generate for a phantom HTLC which
has no corresponding channel. Thus, instead, we have to fall back
to `incorrect_cltv_expiry`.

Fixes lightningdevkit#1879
  • Loading branch information
TheBlueMatt committed Dec 2, 2022
1 parent 7d9a7c0 commit 80b1f21
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 2 deletions.
7 changes: 5 additions & 2 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2278,10 +2278,13 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
}
chan_update_opt
} else {
if (msg.cltv_expiry as u64) < (*outgoing_cltv_value) as u64 + MIN_CLTV_EXPIRY_DELTA as u64 { // incorrect_cltv_expiry
if (msg.cltv_expiry as u64) < (*outgoing_cltv_value) as u64 + MIN_CLTV_EXPIRY_DELTA as u64 {
// We really should set `incorrect_cltv_expiry` here but as we're not
// forwarding over a real channel we can't generate a channel_update
// for it. Instead we just return a generic temporary_node_failure.
break Some((
"Forwarding node has tampered with the intended HTLC values or origin node has an obsolete cltv_expiry_delta",
0x1000 | 13, None,
0x2000 | 2, None,
));
}
None
Expand Down
41 changes: 41 additions & 0 deletions lightning/src/ln/onion_route_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,47 @@ fn test_phantom_failure_too_low_cltv() {
expect_payment_failed_conditions(&nodes[0], payment_hash, true, fail_conditions);
}

#[test]
fn test_phantom_failure_modified_cltv() {
// Test that we fail back phantoms if the upstream node fiddled with the CLTV too much with the
// correct error code.
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);

let channel = create_announced_chan_between_nodes(&nodes, 0, 1, channelmanager::provided_init_features(), channelmanager::provided_init_features());

// Get the route.
let recv_value_msat = 10_000;
let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[1], Some(recv_value_msat));
let (mut route, phantom_scid) = get_phantom_route!(nodes, recv_value_msat, channel);

// Route the HTLC through to the destination.
nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), PaymentId(payment_hash.0)).unwrap();
check_added_monitors!(nodes[0], 1);
let update_0 = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
let mut update_add = update_0.update_add_htlcs[0].clone();

// Modify the route to have a too-low cltv.
update_add.cltv_expiry -= 10;

nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &update_add);
commitment_signed_dance!(nodes[1], nodes[0], &update_0.commitment_signed, false, true);

let update_1 = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
assert!(update_1.update_fail_htlcs.len() == 1);
let fail_msg = update_1.update_fail_htlcs[0].clone();
nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &fail_msg);
commitment_signed_dance!(nodes[0], nodes[1], update_1.commitment_signed, false);

// Ensure the payment fails with the expected error.
let mut fail_conditions = PaymentFailedConditions::new()
.blamed_scid(phantom_scid)
.expected_htlc_error_data(0x2000 | 2, &[]);
expect_payment_failed_conditions(&nodes[0], payment_hash, false, fail_conditions);
}

#[test]
fn test_phantom_failure_too_low_recv_amt() {
let chanmon_cfgs = create_chanmon_cfgs(2);
Expand Down

0 comments on commit 80b1f21

Please sign in to comment.