Skip to content

Commit 1a0d4dc

Browse files
Automatically fail intercepts back on timeout
1 parent 2976cc2 commit 1a0d4dc

File tree

2 files changed

+73
-12
lines changed

2 files changed

+73
-12
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3060,6 +3060,9 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
30603060
/// Note that LDK does not enforce fee requirements in `amt_to_forward_msat`, and will not stop
30613061
/// you from forwarding more than you received.
30623062
///
3063+
/// Errors if the event was not handled in time, in which case the HTLC was automatically failed
3064+
/// backwards.
3065+
///
30633066
/// [`UserConfig::accept_intercept_htlcs`]: crate::util::config::UserConfig::accept_intercept_htlcs
30643067
/// [`HTLCIntercepted`]: events::Event::HTLCIntercepted
30653068
// TODO: when we move to deciding the best outbound channel at forward time, only take
@@ -3101,6 +3104,9 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
31013104
/// Fails the intercepted HTLC indicated by intercept_id. Should only be called in response to
31023105
/// an [`HTLCIntercepted`] event. See [`ChannelManager::forward_intercepted_htlc`].
31033106
///
3107+
/// Errors if the event was not handled in time, in which case the HTLC was automatically failed
3108+
/// backwards.
3109+
///
31043110
/// [`HTLCIntercepted`]: events::Event::HTLCIntercepted
31053111
pub fn fail_intercepted_htlc(&self, intercept_id: InterceptId) -> Result<(), APIError> {
31063112
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
@@ -6227,7 +6233,6 @@ where
62276233
if height >= htlc.cltv_expiry - HTLC_FAIL_BACK_BUFFER {
62286234
let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec();
62296235
htlc_msat_height_data.extend_from_slice(&byte_utils::be32_to_array(height));
6230-
62316236
timed_out_htlcs.push((HTLCSource::PreviousHopData(htlc.prev_hop.clone()), payment_hash.clone(), HTLCFailReason::Reason {
62326237
failure_code: 0x4000 | 15,
62336238
data: htlc_msat_height_data
@@ -6237,6 +6242,29 @@ where
62376242
});
62386243
!htlcs.is_empty() // Only retain this entry if htlcs has at least one entry.
62396244
});
6245+
6246+
let mut intercepted_htlcs = self.pending_intercepted_htlcs.lock().unwrap();
6247+
intercepted_htlcs.retain(|_, htlc| {
6248+
if height >= htlc.forward_info.outgoing_cltv_value - HTLC_FAIL_BACK_BUFFER {
6249+
let prev_hop_data = HTLCSource::PreviousHopData(HTLCPreviousHopData {
6250+
short_channel_id: htlc.prev_short_channel_id,
6251+
htlc_id: htlc.prev_htlc_id,
6252+
incoming_packet_shared_secret: htlc.forward_info.incoming_shared_secret,
6253+
phantom_shared_secret: None,
6254+
outpoint: htlc.prev_funding_outpoint,
6255+
});
6256+
6257+
let requested_forward_scid /* intercept scid */ = match htlc.forward_info.routing {
6258+
PendingHTLCRouting::Forward { short_channel_id, .. } => short_channel_id,
6259+
_ => unreachable!(),
6260+
};
6261+
timed_out_htlcs.push((prev_hop_data, htlc.forward_info.payment_hash,
6262+
HTLCFailReason::Reason { failure_code: 0x1000 | 14, data: Vec::new() },
6263+
HTLCDestination::InvalidForward { requested_forward_scid }));
6264+
log_trace!(self.logger, "Timing out intercepted HTLC with requested forward scid {}", requested_forward_scid);
6265+
false
6266+
} else { true }
6267+
});
62406268
}
62416269

62426270
self.handle_init_event_channel_failures(failed_channels);

lightning/src/ln/payment_tests.rs

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::chain::channelmonitor::{ANTI_REORG_DELAY, LATENCY_GRACE_PERIOD_BLOCKS
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, InterceptId, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PaymentSendFailure, IDEMPOTENCY_TIMEOUT_TICKS};
19+
use crate::ln::channelmanager::{self, BREAKDOWN_TIMEOUT, ChannelManager, 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;
@@ -1242,6 +1242,13 @@ fn abandoned_send_payment_idempotent() {
12421242
claim_payment(&nodes[0], &[&nodes[1]], second_payment_preimage);
12431243
}
12441244

1245+
#[derive(PartialEq)]
1246+
enum InterceptTest {
1247+
Forward,
1248+
Fail,
1249+
Timeout,
1250+
}
1251+
12451252
#[test]
12461253
fn test_trivial_inflight_htlc_tracking(){
12471254
// In this test, we test three scenarios:
@@ -1377,11 +1384,13 @@ fn intercepted_payment() {
13771384
// Test that detecting an intercept scid on payment forward will signal LDK to generate an
13781385
// intercept event, which the LSP can then use to either (a) open a JIT channel to forward the
13791386
// payment or (b) fail the payment.
1380-
do_test_intercepted_payment(false);
1381-
do_test_intercepted_payment(true);
1387+
do_test_intercepted_payment(InterceptTest::Forward);
1388+
do_test_intercepted_payment(InterceptTest::Fail);
1389+
// Make sure that intercepted payments will be automatically failed back if too many blocks pass.
1390+
do_test_intercepted_payment(InterceptTest::Timeout);
13821391
}
13831392

1384-
fn do_test_intercepted_payment(fail_intercept: bool) {
1393+
fn do_test_intercepted_payment(test: InterceptTest) {
13851394
let chanmon_cfgs = create_chanmon_cfgs(3);
13861395
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
13871396

@@ -1458,7 +1467,7 @@ fn do_test_intercepted_payment(fail_intercept: bool) {
14581467
let unknown_chan_id_err = nodes[1].node.forward_intercepted_htlc(intercept_id, &[42; 32], nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap_err();
14591468
assert_eq!(unknown_chan_id_err , APIError::APIMisuseError { err: format!("Channel with id {:?} not found", [42; 32]) });
14601469

1461-
if fail_intercept {
1470+
if test == InterceptTest::Fail {
14621471
// Ensure we can fail the intercepted payment back.
14631472
nodes[1].node.fail_intercepted_htlc(intercept_id).unwrap();
14641473
expect_pending_htlcs_forwardable_and_htlc_handling_failed_ignore!(nodes[1], vec![HTLCDestination::UnknownNextHop { requested_forward_scid: intercept_scid }]);
@@ -1476,15 +1485,10 @@ fn do_test_intercepted_payment(fail_intercept: bool) {
14761485
.blamed_chan_closed(true)
14771486
.expected_htlc_error_data(0x4000 | 10, &[]);
14781487
expect_payment_failed_conditions(&nodes[0], payment_hash, false, fail_conditions);
1479-
} else {
1488+
} else if test == InterceptTest::Forward {
14801489
// Open the just-in-time channel so the payment can then be forwarded.
14811490
let (_, channel_id) = open_zero_conf_channel(&nodes[1], &nodes[2], None);
14821491

1483-
// Check for unknown intercept id error.
1484-
let unknown_intercept_id = InterceptId([42; 32]);
1485-
let unknown_intercept_id_err = nodes[1].node.forward_intercepted_htlc(unknown_intercept_id, &channel_id, nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap_err();
1486-
assert_eq!(unknown_intercept_id_err , APIError::APIMisuseError { err: format!("Payment with intercept id {:?} not found", unknown_intercept_id.0) });
1487-
14881492
// Finally, forward the intercepted payment through and claim it.
14891493
nodes[1].node.forward_intercepted_htlc(intercept_id, &channel_id, nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap();
14901494
expect_pending_htlcs_forwardable!(nodes[1]);
@@ -1522,5 +1526,34 @@ fn do_test_intercepted_payment(fail_intercept: bool) {
15221526
},
15231527
_ => panic!("Unexpected event")
15241528
}
1529+
} else if test == InterceptTest::Timeout {
1530+
let mut block = Block {
1531+
header: BlockHeader { version: 0x20000000, prev_blockhash: nodes[0].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 },
1532+
txdata: vec![],
1533+
};
1534+
connect_block(&nodes[0], &block);
1535+
connect_block(&nodes[1], &block);
1536+
let block_count = 183; // find_route adds a random CLTV offset, so hardcode rather than summing consts
1537+
for _ in 0..block_count {
1538+
block.header.prev_blockhash = block.block_hash();
1539+
connect_block(&nodes[0], &block);
1540+
connect_block(&nodes[1], &block);
1541+
}
1542+
expect_pending_htlcs_forwardable_and_htlc_handling_failed!(nodes[1], vec![HTLCDestination::InvalidForward { requested_forward_scid: intercept_scid }]);
1543+
check_added_monitors!(nodes[1], 1);
1544+
let htlc_timeout_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
1545+
assert!(htlc_timeout_updates.update_add_htlcs.is_empty());
1546+
assert_eq!(htlc_timeout_updates.update_fail_htlcs.len(), 1);
1547+
assert!(htlc_timeout_updates.update_fail_malformed_htlcs.is_empty());
1548+
assert!(htlc_timeout_updates.update_fee.is_none());
1549+
1550+
nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &htlc_timeout_updates.update_fail_htlcs[0]);
1551+
commitment_signed_dance!(nodes[0], nodes[1], htlc_timeout_updates.commitment_signed, false);
1552+
expect_payment_failed!(nodes[0], payment_hash, false, 0x1000 | 14, [0 as u8;0]);
1553+
1554+
// Check for unknown intercept id error.
1555+
let (_, channel_id) = open_zero_conf_channel(&nodes[1], &nodes[2], None);
1556+
let unknown_intercept_id_err = nodes[1].node.forward_intercepted_htlc(intercept_id, &channel_id, nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap_err();
1557+
assert_eq!(unknown_intercept_id_err , APIError::APIMisuseError { err: format!("Payment with intercept id {:?} not found", intercept_id.0) });
15251558
}
15261559
}

0 commit comments

Comments
 (0)