Skip to content

Commit 46bcf8a

Browse files
committed
channeld: channel drain mitigation.
Add new check if we're funder trying to add HTLC, keeping us with enough extra funds to pay for another HTLC the peer might add. Signed-off-by: Rusty Russell <[email protected]>
1 parent 7aab045 commit 46bcf8a

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

channeld/full_channel.c

+61
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,58 @@ static struct amount_sat fee_for_htlcs(const struct channel *channel,
391391
return commit_tx_base_fee(feerate, untrimmed);
392392
}
393393

394+
/* There is a corner case where the funder can spend so much that the
395+
* non-funder can't add any non-dust HTLCs (since the funder would
396+
* have to pay the additional fee, but it can't afford to). This
397+
* leads to the channel starving at the feast! This was reported by
398+
* ACINQ's @t-bast
399+
* (https://github.com/lightningnetwork/lightning-rfc/issues/728) and
400+
* demonstrated with c-lightning by @m-schmook
401+
* (https://github.com/ElementsProject/lightning/pull/3498).
402+
*
403+
* To mostly avoid this situation, at least from our side, we apply an
404+
* additional constraint when we're funder trying to add an HTLC: make
405+
* sure we can afford one more HTLC, even if fees increase 50%.
406+
*
407+
* We could do this for the peer, as well, by rejecting their HTLC
408+
* immediately in this case. But rejecting a remote HTLC here causes
409+
* us to get upset with them and close the channel: we're not well
410+
* architected to reject HTLCs in channeld (it's usually lightningd's
411+
* job, but it doesn't have all the channel balance change calculation
412+
* logic. So we look after ourselves for now, and hope other nodes start
413+
* self-regulating too. */
414+
static bool local_funder_has_fee_headroom(const struct channel *channel,
415+
struct amount_msat remainder,
416+
const struct htlc **committed,
417+
const struct htlc **adding,
418+
const struct htlc **removing)
419+
{
420+
u32 feerate = channel_feerate(channel, LOCAL);
421+
size_t untrimmed;
422+
struct amount_sat fee;
423+
424+
assert(channel->funder == LOCAL);
425+
426+
/* How many untrimmed at current feerate? Increasing feerate can
427+
* only *reduce* this number, so use current feerate here! */
428+
untrimmed = num_untrimmed_htlcs(LOCAL, channel->config[LOCAL].dust_limit,
429+
feerate,
430+
committed, adding, removing);
431+
432+
/* Now, how much would it cost us if feerate increases 50% and we added
433+
* another HTLC? */
434+
fee = commit_tx_base_fee(feerate + feerate/2, untrimmed + 1);
435+
if (amount_msat_greater_eq_sat(remainder, fee))
436+
return true;
437+
438+
status_debug("Adding HTLC would leave us only %s:"
439+
" we need %s for another HTLC if fees increase 50%% to %u",
440+
type_to_string(tmpctx, struct amount_msat, &remainder),
441+
type_to_string(tmpctx, struct amount_sat, &fee),
442+
feerate);
443+
return false;
444+
}
445+
394446
static enum channel_add_err add_htlc(struct channel *channel,
395447
enum htlc_state state,
396448
u64 id,
@@ -572,6 +624,15 @@ static enum channel_add_err add_htlc(struct channel *channel,
572624
&remainder));
573625
return CHANNEL_ERR_CHANNEL_CAPACITY_EXCEEDED;
574626
}
627+
628+
if (sender == LOCAL
629+
&& !local_funder_has_fee_headroom(channel,
630+
remainder,
631+
committed,
632+
adding,
633+
removing)) {
634+
return CHANNEL_ERR_CHANNEL_CAPACITY_EXCEEDED;
635+
}
575636
}
576637

577638
/* Try not to add a payment which will take funder into fees

tests/test_pay.py

-1
Original file line numberDiff line numberDiff line change
@@ -2333,7 +2333,6 @@ def test_channel_drainage(node_factory, bitcoind):
23332333
l2.rpc.waitsendpay(payment_hash, TIMEOUT)
23342334

23352335

2336-
@pytest.mark.xfail(strict=True)
23372336
def test_lockup_drain(node_factory, bitcoind):
23382337
"""Try to get channel into a state where funder can't afford fees on additional HTLC, so fundee can't add HTLC"""
23392338
l1, l2 = node_factory.line_graph(2, opts={'may_reconnect': True})

0 commit comments

Comments
 (0)