Skip to content

Commit 0759af8

Browse files
Automatically fail intercepts back on timeout
1 parent b7ed4fb commit 0759af8

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
pub fn forward_intercepted_htlc(&self, intercept_id: InterceptId, next_hop_scid: u64, _next_node_id: PublicKey, amt_to_forward_msat: u64) -> Result<(), APIError> {
31763179
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
@@ -3208,6 +3211,9 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
32083211
/// Fails the intercepted HTLC indicated by intercept_id. Should only be called in response to
32093212
/// a [`HTLCIntercepted`] event. See [`ChannelManager::forward_intercepted_htlc`].
32103213
///
3214+
/// Errors if the event was not handled in time, in which case the HTLC was automatically failed
3215+
/// backwards.
3216+
///
32113217
/// [`HTLCIntercepted`]: events::Event::HTLCIntercepted
32123218
pub fn fail_intercepted_htlc(&self, intercept_id: InterceptId) -> Result<(), APIError> {
32133219
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
@@ -6206,25 +6212,46 @@ where
62066212
});
62076213

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

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)