Skip to content

Commit ea47426

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 7d97d1e commit ea47426

File tree

3 files changed

+104
-3
lines changed

3 files changed

+104
-3
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ enum PendingForwardReceiveHTLCInfo {
7474
},
7575
Receive {
7676
payment_data: Option<msgs::FinalOnionHopData>,
77+
incoming_cltv_expiry: u32, // Used to track when we should expire pending HTLCs that go unclaimed
7778
},
7879
}
7980

@@ -123,6 +124,7 @@ struct ClaimableHTLC {
123124
src: HTLCPreviousHopData,
124125
value: u64,
125126
payment_data: Option<msgs::FinalOnionHopData>,
127+
cltv_expiry: u32,
126128
}
127129

128130
/// Tracks the inbound corresponding to an outbound HTLC
@@ -1032,7 +1034,10 @@ impl<ChanSigner: ChannelKeys> ChannelManager<ChanSigner> {
10321034
// delay) once they've send us a commitment_signed!
10331035

10341036
PendingHTLCStatus::Forward(PendingHTLCInfo {
1035-
type_data: PendingForwardReceiveHTLCInfo::Receive { payment_data },
1037+
type_data: PendingForwardReceiveHTLCInfo::Receive {
1038+
payment_data,
1039+
incoming_cltv_expiry: msg.cltv_expiry,
1040+
},
10361041
payment_hash: msg.payment_hash.clone(),
10371042
incoming_shared_secret: shared_secret,
10381043
amt_to_forward: next_hop_data.amt_to_forward,
@@ -1640,7 +1645,7 @@ impl<ChanSigner: ChannelKeys> ChannelManager<ChanSigner> {
16401645
for forward_info in pending_forwards.drain(..) {
16411646
match forward_info {
16421647
HTLCForwardInfo::AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info: PendingHTLCInfo {
1643-
type_data: PendingForwardReceiveHTLCInfo::Receive { payment_data },
1648+
type_data: PendingForwardReceiveHTLCInfo::Receive { payment_data, incoming_cltv_expiry },
16441649
incoming_shared_secret, payment_hash, amt_to_forward, .. }, } => {
16451650
let prev_hop_data = HTLCPreviousHopData {
16461651
short_channel_id: prev_short_channel_id,
@@ -1656,6 +1661,7 @@ impl<ChanSigner: ChannelKeys> ChannelManager<ChanSigner> {
16561661
src: prev_hop_data,
16571662
value: amt_to_forward,
16581663
payment_data: payment_data.clone(),
1664+
cltv_expiry: incoming_cltv_expiry,
16591665
});
16601666
if let &Some(ref data) = &payment_data {
16611667
for htlc in htlcs.iter() {
@@ -2808,6 +2814,7 @@ impl<ChanSigner: ChannelKeys> ChainListener for ChannelManager<ChanSigner> {
28082814
log_trace!(self, "Block {} at height {} connected with {} txn matched", header_hash, height, txn_matched.len());
28092815
let _ = self.total_consistency_lock.read().unwrap();
28102816
let mut failed_channels = Vec::new();
2817+
let mut timed_out_htlcs = Vec::new();
28112818
{
28122819
let mut channel_lock = self.channel_state.lock().unwrap();
28132820
let channel_state = channel_lock.borrow_parts();
@@ -2874,10 +2881,29 @@ impl<ChanSigner: ChannelKeys> ChainListener for ChannelManager<ChanSigner> {
28742881
}
28752882
true
28762883
});
2884+
2885+
channel_state.claimable_htlcs.retain(|(payment_hash, _), htlcs| {
2886+
htlcs.retain(|htlc| {
2887+
if height >= htlc.cltv_expiry - CLTV_CLAIM_BUFFER - LATENCY_GRACE_PERIOD_BLOCKS {
2888+
timed_out_htlcs.push((HTLCSource::PreviousHopData(htlc.src.clone()), payment_hash.clone(), htlc.value));
2889+
false
2890+
} else { true }
2891+
});
2892+
!htlcs.is_empty()
2893+
});
28772894
}
28782895
for failure in failed_channels.drain(..) {
28792896
self.finish_force_close_channel(failure);
28802897
}
2898+
2899+
for (source, payment_hash, value) in timed_out_htlcs.drain(..) {
2900+
// Call it preimage_unknown as the issue, ultimately, is that the user failed to
2901+
// provide us a preimage within the cltv_expiry time window.
2902+
self.fail_htlc_backwards_internal(self.channel_state.lock().unwrap(), source, &payment_hash, HTLCFailReason::Reason {
2903+
failure_code: 0x4000 | 15,
2904+
data: byte_utils::be64_to_array(value).to_vec()
2905+
});
2906+
}
28812907
self.latest_block_height.store(height as usize, Ordering::Release);
28822908
*self.last_block_hash.try_lock().expect("block_(dis)connected must not be called in parallel") = header_hash;
28832909
}
@@ -3214,9 +3240,10 @@ impl Writeable for PendingHTLCInfo {
32143240
onion_packet.write(writer)?;
32153241
short_channel_id.write(writer)?;
32163242
},
3217-
&PendingForwardReceiveHTLCInfo::Receive { ref payment_data } => {
3243+
&PendingForwardReceiveHTLCInfo::Receive { ref payment_data, ref incoming_cltv_expiry } => {
32183244
1u8.write(writer)?;
32193245
payment_data.write(writer)?;
3246+
incoming_cltv_expiry.write(writer)?;
32203247
},
32213248
}
32223249
self.incoming_shared_secret.write(writer)?;
@@ -3237,6 +3264,7 @@ impl<R: ::std::io::Read> Readable<R> for PendingHTLCInfo {
32373264
},
32383265
1u8 => PendingForwardReceiveHTLCInfo::Receive {
32393266
payment_data: Readable::read(reader)?,
3267+
incoming_cltv_expiry: Readable::read(reader)?,
32403268
},
32413269
_ => return Err(DecodeError::InvalidValue),
32423270
},
@@ -3446,6 +3474,7 @@ impl<ChanSigner: ChannelKeys + Writeable> Writeable for ChannelManager<ChanSigne
34463474
htlc.src.write(writer)?;
34473475
htlc.value.write(writer)?;
34483476
htlc.payment_data.write(writer)?;
3477+
htlc.cltv_expiry.write(writer)?;
34493478
}
34503479
}
34513480

@@ -3591,6 +3620,7 @@ impl<'a, R : ::std::io::Read, ChanSigner: ChannelKeys + Readable<R>> ReadableArg
35913620
src: Readable::read(reader)?,
35923621
value: Readable::read(reader)?,
35933622
payment_data: Readable::read(reader)?,
3623+
cltv_expiry: Readable::read(reader)?,
35943624
});
35953625
}
35963626
claimable_htlcs.insert(payment_hash, previous_hops);

lightning/src/ln/functional_tests.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2106,6 +2106,15 @@ fn claim_htlc_outputs_single_tx() {
21062106
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
21072107
nodes[0].block_notifier.block_connected(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 200);
21082108
nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 200);
2109+
2110+
// Expect pending failures, but we don't bother trying to update the channel state with them.
2111+
let events = nodes[0].node.get_and_clear_pending_events();
2112+
assert_eq!(events.len(), 1);
2113+
match events[0] {
2114+
Event::PendingHTLCsForwardable { .. } => { },
2115+
_ => panic!("Unexpected event"),
2116+
};
2117+
21092118
connect_blocks(&nodes[1].block_notifier, ANTI_REORG_DELAY - 1, 200, true, header.bitcoin_hash());
21102119

21112120
let events = nodes[1].node.get_and_clear_pending_events();
@@ -3338,6 +3347,47 @@ fn test_drop_messages_peer_disconnect_dual_htlc() {
33383347
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_2, 1_000_000);
33393348
}
33403349

3350+
3351+
#[test]
3352+
fn test_htlc_timeout() {
3353+
// If the user fails to claim/fail an HTLC within the HTLC CLTV timeout we fail it for them
3354+
// to avoid our counterparty failing the channel.
3355+
let nodes = create_network(2, &[None, None]);
3356+
3357+
create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::supported(), InitFeatures::supported());
3358+
let (_, our_payment_hash) = route_payment(&nodes[0], &[&nodes[1]], 100000);
3359+
3360+
let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
3361+
nodes[0].block_notifier.block_connected_checked(&header, 101, &[], &[]);
3362+
nodes[1].block_notifier.block_connected_checked(&header, 101, &[], &[]);
3363+
for i in 102..TEST_FINAL_CLTV + 100 + 1 - CLTV_CLAIM_BUFFER - LATENCY_GRACE_PERIOD_BLOCKS {
3364+
header.prev_blockhash = header.bitcoin_hash();
3365+
nodes[0].block_notifier.block_connected_checked(&header, i, &[], &[]);
3366+
nodes[1].block_notifier.block_connected_checked(&header, i, &[], &[]);
3367+
}
3368+
3369+
expect_pending_htlcs_forwardable!(nodes[1]);
3370+
3371+
check_added_monitors!(nodes[1], 1);
3372+
let htlc_timeout_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
3373+
assert!(htlc_timeout_updates.update_add_htlcs.is_empty());
3374+
assert_eq!(htlc_timeout_updates.update_fail_htlcs.len(), 1);
3375+
assert!(htlc_timeout_updates.update_fail_malformed_htlcs.is_empty());
3376+
assert!(htlc_timeout_updates.update_fee.is_none());
3377+
3378+
nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &htlc_timeout_updates.update_fail_htlcs[0]);
3379+
commitment_signed_dance!(nodes[0], nodes[1], htlc_timeout_updates.commitment_signed, false);
3380+
let events = nodes[0].node.get_and_clear_pending_events();
3381+
match events[0] {
3382+
Event::PaymentFailed { payment_hash, rejected_by_dest, error_code } => {
3383+
assert_eq!(payment_hash, our_payment_hash);
3384+
assert!(rejected_by_dest);
3385+
assert_eq!(error_code.unwrap(), 0x4000 | 15);
3386+
},
3387+
_ => panic!("Unexpected event"),
3388+
}
3389+
}
3390+
33413391
#[test]
33423392
fn test_invalid_channel_announcement() {
33433393
//Test BOLT 7 channel_announcement msg requirement for final node, gather data to build customed channel_announcement msgs
@@ -6479,6 +6529,15 @@ fn test_bump_penalty_txn_on_revoked_htlcs() {
64796529

64806530
// Broadcast set of revoked txn on A
64816531
let header_128 = connect_blocks(&nodes[0].block_notifier, 128, 0, true, header.bitcoin_hash());
6532+
6533+
// Expect pending failures, but we don't bother trying to update the channel state with them.
6534+
let events = nodes[0].node.get_and_clear_pending_events();
6535+
assert_eq!(events.len(), 1);
6536+
match events[0] {
6537+
Event::PendingHTLCsForwardable { .. } => { },
6538+
_ => panic!("Unexpected event"),
6539+
};
6540+
64826541
let header_129 = BlockHeader { version: 0x20000000, prev_blockhash: header_128, merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
64836542
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);
64846543
let first;
@@ -6795,6 +6854,15 @@ fn test_bump_txn_sanitize_tracking_maps() {
67956854

67966855
// Broadcast set of revoked txn on A
67976856
let header_128 = connect_blocks(&nodes[0].block_notifier, 128, 0, false, Default::default());
6857+
6858+
// Expect pending failures, but we don't bother trying to update the channel state with them.
6859+
let events = nodes[0].node.get_and_clear_pending_events();
6860+
assert_eq!(events.len(), 1);
6861+
match events[0] {
6862+
Event::PendingHTLCsForwardable { .. } => { },
6863+
_ => panic!("Unexpected event"),
6864+
};
6865+
67986866
let header_129 = BlockHeader { version: 0x20000000, prev_blockhash: header_128, merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
67996867
nodes[0].block_notifier.block_connected(&Block { header: header_129, txdata: vec![revoked_local_txn[0].clone()] }, 129);
68006868
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
@@ -57,6 +57,9 @@ pub enum Event {
5757
/// ChannelManager::fail_htlc_backwards to free up resources for this HTLC.
5858
/// The amount paid should be considered 'incorrect' when it is less than or more than twice
5959
/// the amount expected.
60+
/// If you fail to call either ChannelManager::claim_funds of
61+
/// ChannelManager::fail_htlc_backwards within the HTLC's timeout, the HTLC will be
62+
/// automatically failed with PaymentFailReason::PreimageUnknown.
6063
PaymentReceived {
6164
/// The hash for which the preimage should be handed to the ChannelManager.
6265
payment_hash: PaymentHash,

0 commit comments

Comments
 (0)