Skip to content

Commit d8b74f0

Browse files
committed
Time out incoming HTLCs when we reach cltv_expiry (+ test)
We only do this for incoming HTLCs directly as we rely on channel closure and HTLC-Timeout broadcast to fail any HTLCs which we relayed onwards where our next-hop doesn't update_fail in time.
1 parent 7ec16e5 commit d8b74f0

File tree

3 files changed

+106
-4
lines changed

3 files changed

+106
-4
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ enum PendingHTLCRouting {
7676
},
7777
Receive {
7878
payment_data: Option<msgs::FinalOnionHopData>,
79+
incoming_cltv_expiry: u32, // Used to track when we should expire pending HTLCs that go unclaimed
7980
},
8081
}
8182

@@ -129,6 +130,7 @@ struct ClaimableHTLC {
129130
/// payment_secret which prevents path-probing attacks and can associate different HTLCs which
130131
/// are part of the same payment.
131132
payment_data: Option<msgs::FinalOnionHopData>,
133+
cltv_expiry: u32,
132134
}
133135

134136
/// Tracks the inbound corresponding to an outbound HTLC
@@ -1063,7 +1065,10 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
10631065
// delay) once they've send us a commitment_signed!
10641066

10651067
PendingHTLCStatus::Forward(PendingHTLCInfo {
1066-
routing: PendingHTLCRouting::Receive { payment_data },
1068+
routing: PendingHTLCRouting::Receive {
1069+
payment_data,
1070+
incoming_cltv_expiry: msg.cltv_expiry,
1071+
},
10671072
payment_hash: msg.payment_hash.clone(),
10681073
incoming_shared_secret: shared_secret,
10691074
amt_to_forward: next_hop_data.amt_to_forward,
@@ -1705,7 +1710,7 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
17051710
for forward_info in pending_forwards.drain(..) {
17061711
match forward_info {
17071712
HTLCForwardInfo::AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info: PendingHTLCInfo {
1708-
routing: PendingHTLCRouting::Receive { payment_data },
1713+
routing: PendingHTLCRouting::Receive { payment_data, incoming_cltv_expiry },
17091714
incoming_shared_secret, payment_hash, amt_to_forward, .. }, } => {
17101715
let prev_hop = HTLCPreviousHopData {
17111716
short_channel_id: prev_short_channel_id,
@@ -1722,6 +1727,7 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
17221727
prev_hop,
17231728
value: amt_to_forward,
17241729
payment_data: payment_data.clone(),
1730+
cltv_expiry: incoming_cltv_expiry,
17251731
});
17261732
if let &Some(ref data) = &payment_data {
17271733
for htlc in htlcs.iter() {
@@ -2965,6 +2971,7 @@ impl<ChanSigner: ChannelKeys, M: Deref + Sync + Send, T: Deref + Sync + Send, K:
29652971
log_trace!(self, "Block {} at height {} connected with {} txn matched", header_hash, height, txn_matched.len());
29662972
let _ = self.total_consistency_lock.read().unwrap();
29672973
let mut failed_channels = Vec::new();
2974+
let mut timed_out_htlcs = Vec::new();
29682975
{
29692976
let mut channel_lock = self.channel_state.lock().unwrap();
29702977
let channel_state = &mut *channel_lock;
@@ -3033,10 +3040,29 @@ impl<ChanSigner: ChannelKeys, M: Deref + Sync + Send, T: Deref + Sync + Send, K:
30333040
}
30343041
true
30353042
});
3043+
3044+
channel_state.claimable_htlcs.retain(|&(ref payment_hash, _), htlcs| {
3045+
htlcs.retain(|htlc| {
3046+
if height >= htlc.cltv_expiry - CLTV_CLAIM_BUFFER - LATENCY_GRACE_PERIOD_BLOCKS {
3047+
timed_out_htlcs.push((HTLCSource::PreviousHopData(htlc.prev_hop.clone()), payment_hash.clone(), htlc.value));
3048+
false
3049+
} else { true }
3050+
});
3051+
!htlcs.is_empty()
3052+
});
30363053
}
30373054
for failure in failed_channels.drain(..) {
30383055
self.finish_force_close_channel(failure);
30393056
}
3057+
3058+
for (source, payment_hash, value) in timed_out_htlcs.drain(..) {
3059+
// Call it preimage_unknown as the issue, ultimately, is that the user failed to
3060+
// provide us a preimage within the cltv_expiry time window.
3061+
self.fail_htlc_backwards_internal(self.channel_state.lock().unwrap(), source, &payment_hash, HTLCFailReason::Reason {
3062+
failure_code: 0x4000 | 15,
3063+
data: byte_utils::be64_to_array(value).to_vec()
3064+
});
3065+
}
30403066
self.latest_block_height.store(height as usize, Ordering::Release);
30413067
*self.last_block_hash.try_lock().expect("block_(dis)connected must not be called in parallel") = header_hash;
30423068
loop {
@@ -3327,9 +3353,10 @@ impl Writeable for PendingHTLCInfo {
33273353
onion_packet.write(writer)?;
33283354
short_channel_id.write(writer)?;
33293355
},
3330-
&PendingHTLCRouting::Receive { ref payment_data } => {
3356+
&PendingHTLCRouting::Receive { ref payment_data, ref incoming_cltv_expiry } => {
33313357
1u8.write(writer)?;
33323358
payment_data.write(writer)?;
3359+
incoming_cltv_expiry.write(writer)?;
33333360
},
33343361
}
33353362
self.incoming_shared_secret.write(writer)?;
@@ -3350,6 +3377,7 @@ impl Readable for PendingHTLCInfo {
33503377
},
33513378
1u8 => PendingHTLCRouting::Receive {
33523379
payment_data: Readable::read(reader)?,
3380+
incoming_cltv_expiry: Readable::read(reader)?,
33533381
},
33543382
_ => return Err(DecodeError::InvalidValue),
33553383
},
@@ -3422,7 +3450,8 @@ impl_writeable!(HTLCPreviousHopData, 0, {
34223450
impl_writeable!(ClaimableHTLC, 0, {
34233451
prev_hop,
34243452
value,
3425-
payment_data
3453+
payment_data,
3454+
cltv_expiry
34263455
});
34273456

34283457
impl Writeable for HTLCSource {

lightning/src/ln/functional_tests.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2323,6 +2323,15 @@ fn claim_htlc_outputs_single_tx() {
23232323
check_added_monitors!(nodes[0], 1);
23242324
nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 200);
23252325
check_added_monitors!(nodes[1], 1);
2326+
2327+
// Expect pending failures, but we don't bother trying to update the channel state with them.
2328+
let events = nodes[0].node.get_and_clear_pending_events();
2329+
assert_eq!(events.len(), 1);
2330+
match events[0] {
2331+
Event::PendingHTLCsForwardable { .. } => { },
2332+
_ => panic!("Unexpected event"),
2333+
};
2334+
23262335
connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 200, true, header.bitcoin_hash());
23272336

23282337
let events = nodes[1].node.get_and_clear_pending_events();
@@ -3680,6 +3689,49 @@ fn test_drop_messages_peer_disconnect_dual_htlc() {
36803689
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_2, 1_000_000);
36813690
}
36823691

3692+
#[test]
3693+
fn test_htlc_timeout() {
3694+
// If the user fails to claim/fail an HTLC within the HTLC CLTV timeout we fail it for them
3695+
// to avoid our counterparty failing the channel.
3696+
let chanmon_cfgs = create_chanmon_cfgs(2);
3697+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
3698+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
3699+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
3700+
3701+
create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::supported(), InitFeatures::supported());
3702+
let (_, our_payment_hash) = route_payment(&nodes[0], &[&nodes[1]], 100000);
3703+
3704+
let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
3705+
nodes[0].block_notifier.block_connected_checked(&header, 101, &[], &[]);
3706+
nodes[1].block_notifier.block_connected_checked(&header, 101, &[], &[]);
3707+
for i in 102..TEST_FINAL_CLTV + 100 + 1 - CLTV_CLAIM_BUFFER - LATENCY_GRACE_PERIOD_BLOCKS {
3708+
header.prev_blockhash = header.bitcoin_hash();
3709+
nodes[0].block_notifier.block_connected_checked(&header, i, &[], &[]);
3710+
nodes[1].block_notifier.block_connected_checked(&header, i, &[], &[]);
3711+
}
3712+
3713+
expect_pending_htlcs_forwardable!(nodes[1]);
3714+
3715+
check_added_monitors!(nodes[1], 1);
3716+
let htlc_timeout_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
3717+
assert!(htlc_timeout_updates.update_add_htlcs.is_empty());
3718+
assert_eq!(htlc_timeout_updates.update_fail_htlcs.len(), 1);
3719+
assert!(htlc_timeout_updates.update_fail_malformed_htlcs.is_empty());
3720+
assert!(htlc_timeout_updates.update_fee.is_none());
3721+
3722+
nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &htlc_timeout_updates.update_fail_htlcs[0]);
3723+
commitment_signed_dance!(nodes[0], nodes[1], htlc_timeout_updates.commitment_signed, false);
3724+
let events = nodes[0].node.get_and_clear_pending_events();
3725+
match events[0] {
3726+
Event::PaymentFailed { payment_hash, rejected_by_dest, error_code } => {
3727+
assert_eq!(payment_hash, our_payment_hash);
3728+
assert!(rejected_by_dest);
3729+
assert_eq!(error_code.unwrap(), 0x4000 | 15);
3730+
},
3731+
_ => panic!("Unexpected event"),
3732+
}
3733+
}
3734+
36833735
#[test]
36843736
fn test_invalid_channel_announcement() {
36853737
//Test BOLT 7 channel_announcement msg requirement for final node, gather data to build customed channel_announcement msgs
@@ -7159,6 +7211,15 @@ fn test_bump_penalty_txn_on_revoked_htlcs() {
71597211

71607212
// Broadcast set of revoked txn on A
71617213
let header_128 = connect_blocks(&nodes[0].block_notifier, 128, 0, true, header.bitcoin_hash());
7214+
7215+
// Expect pending failures, but we don't bother trying to update the channel state with them.
7216+
let events = nodes[0].node.get_and_clear_pending_events();
7217+
assert_eq!(events.len(), 1);
7218+
match events[0] {
7219+
Event::PendingHTLCsForwardable { .. } => { },
7220+
_ => panic!("Unexpected event"),
7221+
};
7222+
71627223
let header_129 = BlockHeader { version: 0x20000000, prev_blockhash: header_128, merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
71637224
nodes[0].block_notifier.block_connected(&Block { header: header_129, txdata: vec![revoked_local_txn[0].clone(), revoked_htlc_txn[0].clone(), revoked_htlc_txn[1].clone()] }, 129);
71647225
let first;
@@ -7491,6 +7552,15 @@ fn test_bump_txn_sanitize_tracking_maps() {
74917552

74927553
// Broadcast set of revoked txn on A
74937554
let header_128 = connect_blocks(&nodes[0].block_notifier, 128, 0, false, Default::default());
7555+
7556+
// Expect pending failures, but we don't bother trying to update the channel state with them.
7557+
let events = nodes[0].node.get_and_clear_pending_events();
7558+
assert_eq!(events.len(), 1);
7559+
match events[0] {
7560+
Event::PendingHTLCsForwardable { .. } => { },
7561+
_ => panic!("Unexpected event"),
7562+
};
7563+
74947564
let header_129 = BlockHeader { version: 0x20000000, prev_blockhash: header_128, merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
74957565
nodes[0].block_notifier.block_connected(&Block { header: header_129, txdata: vec![revoked_local_txn[0].clone()] }, 129);
74967566
check_closed_broadcast!(nodes[0], false);

lightning/src/util/events.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ pub enum Event {
5555
/// ChannelManager::fail_htlc_backwards to free up resources for this HTLC.
5656
/// The amount paid should be considered 'incorrect' when it is less than or more than twice
5757
/// the amount expected.
58+
/// If you fail to call either ChannelManager::claim_funds of
59+
/// ChannelManager::fail_htlc_backwards within the HTLC's timeout, the HTLC will be
60+
/// automatically failed with PaymentFailReason::PreimageUnknown.
5861
PaymentReceived {
5962
/// The hash for which the preimage should be handed to the ChannelManager.
6063
payment_hash: PaymentHash,

0 commit comments

Comments
 (0)