Skip to content

Commit ee70d50

Browse files
Automatically fail intercepts back on timeout
1 parent 7238c24 commit ee70d50

File tree

2 files changed

+83
-19
lines changed

2 files changed

+83
-19
lines changed

lightning/src/ln/channelmanager.rs

+34-7
Original file line numberDiff line numberDiff line change
@@ -3171,6 +3171,9 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
31713171
///
31723172
/// Note that LDK does not enforce fee requirements in `amt_to_forward_msat`.
31733173
///
3174+
/// Errors if the event was not handled in time, in which case the HTLC was automatically failed
3175+
/// backwards.
3176+
///
31743177
/// [`HTLCIntercepted`]: events::Event::HTLCIntercepted
31753178
// TODO: when we move to deciding the best outbound channel at forward time, only take
31763179
// `next_node_id` and not `next_hop_scid`
@@ -3210,6 +3213,9 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
32103213
/// Fails the intercepted HTLC indicated by intercept_id. Should only be called in response to
32113214
/// a [`HTLCIntercepted`] event. See [`ChannelManager::forward_intercepted_htlc`].
32123215
///
3216+
/// Errors if the event was not handled in time, in which case the HTLC was automatically failed
3217+
/// backwards.
3218+
///
32133219
/// [`HTLCIntercepted`]: events::Event::HTLCIntercepted
32143220
pub fn fail_intercepted_htlc(&self, intercept_id: InterceptId) -> Result<(), APIError> {
32153221
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
@@ -6208,25 +6214,46 @@ where
62086214
});
62096215

62106216
if let Some(height) = height_opt {
6217+
macro_rules! time_out_htlc {
6218+
($amt_msat: expr, $payment_hash: expr, $prev_hop_data: expr) => {
6219+
let mut htlc_msat_height_data = byte_utils::be64_to_array($amt_msat).to_vec();
6220+
htlc_msat_height_data.extend_from_slice(&byte_utils::be32_to_array(height));
6221+
6222+
timed_out_htlcs.push((HTLCSource::PreviousHopData($prev_hop_data), $payment_hash, HTLCFailReason::Reason {
6223+
failure_code: 0x4000 | 15,
6224+
data: htlc_msat_height_data
6225+
}, HTLCDestination::FailedPayment { payment_hash: $payment_hash }));
6226+
}
6227+
}
62116228
channel_state.claimable_htlcs.retain(|payment_hash, (_, htlcs)| {
62126229
htlcs.retain(|htlc| {
62136230
// If height is approaching the number of blocks we think it takes us to get
62146231
// our commitment transaction confirmed before the HTLC expires, plus the
62156232
// number of blocks we generally consider it to take to do a commitment update,
62166233
// just give up on it and fail the HTLC.
62176234
if height >= htlc.cltv_expiry - HTLC_FAIL_BACK_BUFFER {
6218-
let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec();
6219-
htlc_msat_height_data.extend_from_slice(&byte_utils::be32_to_array(height));
6220-
6221-
timed_out_htlcs.push((HTLCSource::PreviousHopData(htlc.prev_hop.clone()), payment_hash.clone(), HTLCFailReason::Reason {
6222-
failure_code: 0x4000 | 15,
6223-
data: htlc_msat_height_data
6224-
}, HTLCDestination::FailedPayment { payment_hash: payment_hash.clone() }));
6235+
time_out_htlc!(htlc.value, *payment_hash, htlc.prev_hop.clone());
62256236
false
62266237
} else { true }
62276238
});
62286239
!htlcs.is_empty() // Only retain this entry if htlcs has at least one entry.
62296240
});
6241+
6242+
let mut intercepted_htlcs = self.pending_intercepted_htlcs.lock().unwrap();
6243+
intercepted_htlcs.retain(|_, htlc| {
6244+
if height >= htlc.forward_info.outgoing_cltv_value - HTLC_FAIL_BACK_BUFFER {
6245+
let prev_hop_data = HTLCPreviousHopData {
6246+
short_channel_id: htlc.prev_short_channel_id,
6247+
htlc_id: htlc.prev_htlc_id,
6248+
incoming_packet_shared_secret: htlc.forward_info.incoming_shared_secret,
6249+
phantom_shared_secret: None,
6250+
outpoint: htlc.prev_funding_outpoint,
6251+
};
6252+
6253+
time_out_htlc!(htlc.forward_info.outgoing_amt_msat, htlc.forward_info.payment_hash, prev_hop_data);
6254+
false
6255+
} else { true }
6256+
});
62306257
}
62316258
}
62326259

lightning/src/ln/payment_tests.rs

+49-12
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ use crate::chain::channelmonitor::{ANTI_REORG_DELAY, ChannelMonitor, LATENCY_GRA
1616
use crate::chain::transaction::OutPoint;
1717
use crate::chain::keysinterface::KeysInterface;
1818
use crate::ln::channel::EXPIRE_PREV_CONFIG_TICKS;
19-
use crate::ln::channelmanager::{self, BREAKDOWN_TIMEOUT, ChannelManager, ChannelManagerReadArgs, InterceptId, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PaymentSendFailure, IDEMPOTENCY_TIMEOUT_TICKS};
19+
use crate::ln::channelmanager::{self, BREAKDOWN_TIMEOUT, ChannelManager, ChannelManagerReadArgs, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PaymentSendFailure, IDEMPOTENCY_TIMEOUT_TICKS};
2020
use crate::ln::msgs;
2121
use crate::ln::msgs::ChannelMessageHandler;
2222
use crate::routing::gossip::RoutingFees;
2323
use crate::routing::router::{find_route, get_route, PaymentParameters, RouteHint, RouteHintHop, RouteParameters};
2424
use crate::util::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider};
25-
use crate::util::test_utils;
25+
use crate::util::{byte_utils, test_utils};
2626
use crate::util::errors::APIError;
2727
use crate::util::enforcing_trait_impls::EnforcingSigner;
2828
use crate::util::ser::{ReadableArgs, Writeable};
@@ -1387,16 +1387,25 @@ fn abandoned_send_payment_idempotent() {
13871387
claim_payment(&nodes[0], &[&nodes[1]], second_payment_preimage);
13881388
}
13891389

1390+
#[derive(PartialEq)]
1391+
enum InterceptTest {
1392+
Forward,
1393+
Fail,
1394+
Timeout,
1395+
}
1396+
13901397
#[test]
13911398
fn intercepted_payment() {
13921399
// Test that detecting an intercept scid on payment forward will signal LDK to generate an
13931400
// intercept event, which the LSP can then use to either (a) open a JIT channel to forward the
13941401
// payment or (b) fail the payment.
1395-
do_test_intercepted_payment(false);
1396-
do_test_intercepted_payment(true);
1402+
do_test_intercepted_payment(InterceptTest::Forward);
1403+
do_test_intercepted_payment(InterceptTest::Fail);
1404+
// Make sure that intercepted payments will be automatically failed back if too many blocks pass.
1405+
do_test_intercepted_payment(InterceptTest::Timeout);
13971406
}
13981407

1399-
fn do_test_intercepted_payment(fail_intercept: bool) {
1408+
fn do_test_intercepted_payment(test: InterceptTest) {
14001409
let chanmon_cfgs = create_chanmon_cfgs(3);
14011410
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
14021411

@@ -1471,7 +1480,7 @@ fn do_test_intercepted_payment(fail_intercept: bool) {
14711480
let unknown_scid_err = nodes[1].node.forward_intercepted_htlc(intercept_id, 4242, nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap_err();
14721481
assert_eq!(unknown_scid_err, APIError::APIMisuseError { err: "Channel with short channel id 4242 not found".to_string() });
14731482

1474-
if fail_intercept {
1483+
if test == InterceptTest::Fail {
14751484
// Ensure we can fail the intercepted payment back.
14761485
nodes[1].node.fail_intercepted_htlc(intercept_id).unwrap();
14771486
expect_pending_htlcs_forwardable_and_htlc_handling_failed_ignore!(nodes[1], vec![HTLCDestination::UnknownNextHop { requested_forward_scid: intercept_scid }]);
@@ -1489,15 +1498,10 @@ fn do_test_intercepted_payment(fail_intercept: bool) {
14891498
.blamed_chan_closed(true)
14901499
.expected_htlc_error_data(0x4000 | 10, &[]);
14911500
expect_payment_failed_conditions(&nodes[0], payment_hash, false, fail_conditions);
1492-
} else {
1501+
} else if test == InterceptTest::Forward {
14931502
// Open the just-in-time channel so the payment can then be forwarded.
14941503
let scid = create_announced_chan_between_nodes(&nodes, 1, 2, channelmanager::provided_init_features(), channelmanager::provided_init_features()).0.contents.short_channel_id;
14951504

1496-
// Check for unknown intercept id error.
1497-
let unknown_intercept_id = InterceptId([42; 32]);
1498-
let unknown_intercept_id_err = nodes[1].node.forward_intercepted_htlc(unknown_intercept_id, scid, nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap_err();
1499-
assert_eq!(unknown_intercept_id_err , APIError::APIMisuseError { err: format!("Payment with intercept id {:?} not found", unknown_intercept_id.0) });
1500-
15011505
// Finally, forward the intercepted payment through and claim it.
15021506
nodes[1].node.forward_intercepted_htlc(intercept_id, scid, nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap();
15031507
expect_pending_htlcs_forwardable!(nodes[1]);
@@ -1535,5 +1539,38 @@ fn do_test_intercepted_payment(fail_intercept: bool) {
15351539
},
15361540
_ => panic!("Unexpected event")
15371541
}
1542+
} else if test == InterceptTest::Timeout {
1543+
let mut block = Block {
1544+
header: BlockHeader { version: 0x20000000, prev_blockhash: nodes[0].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 },
1545+
txdata: vec![],
1546+
};
1547+
connect_block(&nodes[0], &block);
1548+
connect_block(&nodes[1], &block);
1549+
let block_count = 183; // find_route adds a random CLTV offset, so hardcode rather than summing consts
1550+
for _ in 0..block_count {
1551+
block.header.prev_blockhash = block.block_hash();
1552+
connect_block(&nodes[0], &block);
1553+
connect_block(&nodes[1], &block);
1554+
}
1555+
expect_pending_htlcs_forwardable_and_htlc_handling_failed!(nodes[1], vec![HTLCDestination::FailedPayment { payment_hash }]);
1556+
check_added_monitors!(nodes[1], 1);
1557+
let htlc_timeout_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
1558+
assert!(htlc_timeout_updates.update_add_htlcs.is_empty());
1559+
assert_eq!(htlc_timeout_updates.update_fail_htlcs.len(), 1);
1560+
assert!(htlc_timeout_updates.update_fail_malformed_htlcs.is_empty());
1561+
assert!(htlc_timeout_updates.update_fee.is_none());
1562+
1563+
nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &htlc_timeout_updates.update_fail_htlcs[0]);
1564+
commitment_signed_dance!(nodes[0], nodes[1], htlc_timeout_updates.commitment_signed, false);
1565+
// amt_msat as u64, followed by the height at which we failed back above
1566+
let mut expected_failure_data = byte_utils::be64_to_array(amt_msat).to_vec();
1567+
expected_failure_data.extend_from_slice(&byte_utils::be32_to_array(block_count - 1));
1568+
expect_payment_failed!(nodes[0], payment_hash, false, 0x4000 | 15, &expected_failure_data[..]);
1569+
1570+
let scid = create_announced_chan_between_nodes(&nodes, 1, 2, channelmanager::provided_init_features(), channelmanager::provided_init_features()).0.contents.short_channel_id;
1571+
1572+
// Check for unknown intercept id error.
1573+
let unknown_intercept_id_err = nodes[1].node.forward_intercepted_htlc(intercept_id, scid, nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap_err();
1574+
assert_eq!(unknown_intercept_id_err , APIError::APIMisuseError { err: format!("Payment with intercept id {:?} not found", intercept_id.0) });
15381575
}
15391576
}

0 commit comments

Comments
 (0)