Skip to content

Commit 5e998cc

Browse files
committed
Be less aggressive in outbound HTLC CLTV timeout checks
We currently assume our counterparty is naive and misconfigured and may force-close a channel to get an HTLC we just forwarded them. There shouldn't be any reason to do this - we don't have any such bug, and we shouldn't start by assuming our counterparties are buggy. Worse, this results in refusing to forward payments today, failing HTLCs for largely no reason. Instead, we keep a fairly conservative check, but not one which will fail HTLC forwarding spuriously - testing only that the HTLC doesn't expire for a few blocks from now. Fixes #1114.
1 parent fe8c10d commit 5e998cc

File tree

4 files changed

+18
-13
lines changed

4 files changed

+18
-13
lines changed

lightning/src/chain/channelmonitor.rs

-5
Original file line numberDiff line numberDiff line change
@@ -253,18 +253,13 @@ pub const ANTI_REORG_DELAY: u32 = 6;
253253
/// fail this HTLC,
254254
/// 2) if we receive an HTLC within this many blocks of its expiry (plus one to avoid a race
255255
/// condition with the above), we will fail this HTLC without telling the user we received it,
256-
/// 3) if we are waiting on a connection or a channel state update to send an HTLC to a peer, and
257-
/// that HTLC expires within this many blocks, we will simply fail the HTLC instead.
258256
///
259257
/// (1) is all about protecting us - we need enough time to update the channel state before we hit
260258
/// CLTV_CLAIM_BUFFER, at which point we'd go on chain to claim the HTLC with the preimage.
261259
///
262260
/// (2) is the same, but with an additional buffer to avoid accepting an HTLC which is immediately
263261
/// in a race condition between the user connecting a block (which would fail it) and the user
264262
/// providing us the preimage (which would claim it).
265-
///
266-
/// (3) is about our counterparty - we don't want to relay an HTLC to a counterparty when they may
267-
/// end up force-closing the channel on us to claim it.
268263
pub(crate) const HTLC_FAIL_BACK_BUFFER: u32 = CLTV_CLAIM_BUFFER + LATENCY_GRACE_PERIOD_BLOCKS;
269264

270265
// TODO(devrandom) replace this with HolderCommitmentTransaction

lightning/src/ln/channel.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use ln::chan_utils::{CounterpartyCommitmentSecrets, TxCreationKeys, HTLCOutputIn
3232
use ln::chan_utils;
3333
use chain::BestBlock;
3434
use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
35-
use chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, HTLC_FAIL_BACK_BUFFER};
35+
use chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, LATENCY_GRACE_PERIOD_BLOCKS};
3636
use chain::transaction::{OutPoint, TransactionData};
3737
use chain::keysinterface::{Sign, KeysInterface};
3838
use util::ser::{Readable, ReadableArgs, Writeable, Writer, VecWriter};
@@ -4107,7 +4107,10 @@ impl<Signer: Sign> Channel<Signer> {
41074107
pub fn best_block_updated<L: Deref>(&mut self, height: u32, highest_header_time: u32, logger: &L)
41084108
-> Result<(Option<msgs::FundingLocked>, Vec<(HTLCSource, PaymentHash)>), msgs::ErrorMessage> where L::Target: Logger {
41094109
let mut timed_out_htlcs = Vec::new();
4110-
let unforwarded_htlc_cltv_limit = height + HTLC_FAIL_BACK_BUFFER;
4110+
// This mirrors the check in ChannelManager::decode_update_add_htlc_onion, refusing to
4111+
// forward an HTLC when our counterparty should almost certainly just fail it for expiring
4112+
// ~now.
4113+
let unforwarded_htlc_cltv_limit = height + LATENCY_GRACE_PERIOD_BLOCKS;
41114114
self.holding_cell_htlc_updates.retain(|htlc_update| {
41124115
match htlc_update {
41134116
&HTLCUpdateAwaitingACK::AddHTLC { ref payment_hash, ref source, ref cltv_expiry, .. } => {

lightning/src/ln/channelmanager.rs

+12-5
Original file line numberDiff line numberDiff line change
@@ -1850,17 +1850,24 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
18501850
break Some(("Forwarding node has tampered with the intended HTLC values or origin node has an obsolete cltv_expiry_delta", 0x1000 | 13, Some(self.get_channel_update_for_unicast(chan).unwrap())));
18511851
}
18521852
let cur_height = self.best_block.read().unwrap().height() + 1;
1853-
// Theoretically, channel counterparty shouldn't send us a HTLC expiring now, but we want to be robust wrt to counterparty
1854-
// packet sanitization (see HTLC_FAIL_BACK_BUFFER rational)
1853+
// Theoretically, channel counterparty shouldn't send us a HTLC expiring now,
1854+
// but we want to be robust wrt to counterparty packet sanitization (see
1855+
// HTLC_FAIL_BACK_BUFFER rationale).
18551856
if msg.cltv_expiry <= cur_height + HTLC_FAIL_BACK_BUFFER as u32 { // expiry_too_soon
18561857
break Some(("CLTV expiry is too close", 0x1000 | 14, Some(self.get_channel_update_for_unicast(chan).unwrap())));
18571858
}
18581859
if msg.cltv_expiry > cur_height + CLTV_FAR_FAR_AWAY as u32 { // expiry_too_far
18591860
break Some(("CLTV expiry is too far in the future", 21, None));
18601861
}
1861-
// In theory, we would be safe against unintentional channel-closure, if we only required a margin of LATENCY_GRACE_PERIOD_BLOCKS.
1862-
// But, to be safe against policy reception, we use a longer delay.
1863-
if (*outgoing_cltv_value) as u64 <= (cur_height + HTLC_FAIL_BACK_BUFFER) as u64 {
1862+
// If the HTLC expires ~now, don't bother trying to forward it to our
1863+
// counterparty. They should fail it anyway, but we don't want to bother with
1864+
// the round-trips or risk them deciding they definitely want the HTLC and
1865+
// force-closing to ensure they get it if we're offline.
1866+
// We previously had a much more aggressive check here which tried to ensure
1867+
// our counterparty receives an HTLC which has *our* risk threshold met on it,
1868+
// but there is no need to do that, and since we're a bit conservative with our
1869+
// risk threshold it just results in failing to forward payments.
1870+
if (*outgoing_cltv_value) as u64 <= (cur_height + LATENCY_GRACE_PERIOD_BLOCKS) as u64 {
18641871
break Some(("Outgoing CLTV value is too soon", 0x1000 | 14, Some(self.get_channel_update_for_unicast(chan).unwrap())));
18651872
}
18661873

lightning/src/ln/functional_tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4056,7 +4056,7 @@ fn do_test_holding_cell_htlc_add_timeouts(forwarded_htlc: bool) {
40564056
check_added_monitors!(nodes[1], 0);
40574057
}
40584058

4059-
connect_blocks(&nodes[1], TEST_FINAL_CLTV - CLTV_CLAIM_BUFFER - LATENCY_GRACE_PERIOD_BLOCKS);
4059+
connect_blocks(&nodes[1], TEST_FINAL_CLTV - LATENCY_GRACE_PERIOD_BLOCKS);
40604060
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
40614061
assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
40624062
connect_blocks(&nodes[1], 1);

0 commit comments

Comments
 (0)