dusty-htlcs: enforce limit on dusty htlcs

for every new added htlc, check that adding it won't go over our 'dust
budget' (which assumes a slightly higher than current feerate, as this
prevents sudden feerate changes from overshooting our dust budget)

note that if the feerate changes surpass the limits we've set, we
immediately fail the channel.
This commit is contained in:
niftynei 2021-09-28 14:12:01 -05:00 committed by Christian Decker
parent 42e40c1ced
commit b193eb06d3
8 changed files with 182 additions and 1 deletions

View file

@ -1292,6 +1292,14 @@ static void send_commit(struct peer *peer)
if (feerate_changes_done(peer->channel->fee_states, false)) {
u8 *msg;
/* Is this feerate update going to push the committed
* htlcs over our allowed dust limits? */
if (!htlc_dust_ok(peer->channel, feerate_target, REMOTE)
|| !htlc_dust_ok(peer->channel, feerate_target, LOCAL))
/* We fail the channel. Oops */
peer_failed_err(peer->pps, &peer->channel_id,
"Too much dust to update fee");
if (!channel_update_feerate(peer->channel, feerate_target))
status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Could not afford feerate %u"
@ -3360,6 +3368,10 @@ static void handle_offer_htlc(struct peer *peer, const u8 *inmsg)
failwiremsg = towire_temporary_channel_failure(inmsg, get_local_channel_update(inmsg, peer));
failstr = "Too many HTLCs";
goto failed;
case CHANNEL_ERR_DUST_FAILURE:
failwiremsg = towire_temporary_channel_failure(inmsg, get_local_channel_update(inmsg, peer));
failstr = "HTLC too dusty, allowed dust limit reached";
goto failed;
}
/* Shouldn't return anything else! */
abort();

View file

@ -35,6 +35,22 @@ size_t commit_tx_num_untrimmed(const struct htlc **htlcs,
return n;
}
bool commit_tx_amount_trimmed(const struct htlc **htlcs,
u32 feerate_per_kw,
struct amount_sat dust_limit,
bool option_anchor_outputs,
enum side side,
struct amount_msat *amt)
{
for (size_t i = 0; i < tal_count(htlcs); i++) {
if (trim(htlcs[i], feerate_per_kw, dust_limit,
option_anchor_outputs, side))
if (!amount_msat_add(amt, *amt, htlcs[i]->amount))
return false;
}
return true;
}
static void add_offered_htlc_out(struct bitcoin_tx *tx, size_t n,
const struct htlc *htlc,
const struct keyset *keyset,

View file

@ -24,6 +24,26 @@ size_t commit_tx_num_untrimmed(const struct htlc **htlcs,
bool option_anchor_outputs,
enum side side);
/**
* commit_tx_amount_trimmed: what's the sum of trimmed htlc amounts?
* @htlcs: tal_arr of HTLCs
* @feerate_per_kw: feerate to use
* @dust_limit: dust limit below which to trim outputs.
* @option_anchor_outputs: does option_anchor_outputs apply to this channel?
* @side: from which side's point of view
* @amt: returned, total value trimmed from this commitment
*
* We need @side because HTLC fees are different for offered and
* received HTLCs.
*
* Returns false if unable to calculate amount trimmed.
*/
bool commit_tx_amount_trimmed(const struct htlc **htlcs,
u32 feerate_per_kw,
struct amount_sat dust_limit,
bool option_anchor_outputs,
enum side side,
struct amount_msat *amt);
/**
* commit_tx: create (unsigned) commitment tx to spend the funding tx output
* @ctx: context to allocate transaction and @htlc_map from.

View file

@ -7,6 +7,7 @@
#include <common/blockheight_states.h>
#include <common/features.h>
#include <common/fee_states.h>
#include <common/htlc_trim.h>
#include <common/htlc_tx.h>
#include <common/htlc_wire.h>
#include <common/keyset.h>
@ -423,6 +424,37 @@ static struct amount_sat fee_for_htlcs(const struct channel *channel,
return commit_tx_base_fee(feerate, untrimmed, option_anchor_outputs);
}
static bool htlc_dust(const struct channel *channel,
const struct htlc **committed,
const struct htlc **adding,
const struct htlc **removing,
enum side side,
u32 feerate,
struct amount_msat *trim_total)
{
struct amount_sat dust_limit = channel->config[side].dust_limit;
bool option_anchor_outputs = channel_has(channel, OPT_ANCHOR_OUTPUTS);
struct amount_msat trim_rmvd = AMOUNT_MSAT(0);
if (!commit_tx_amount_trimmed(committed, feerate,
dust_limit,
option_anchor_outputs,
side, trim_total))
return false;
if (!commit_tx_amount_trimmed(adding, feerate,
dust_limit,
option_anchor_outputs,
side, trim_total))
return false;
if (!commit_tx_amount_trimmed(removing, feerate,
dust_limit,
option_anchor_outputs,
side, &trim_rmvd))
return false;
return amount_msat_sub(trim_total, *trim_total, trim_rmvd);
}
/*
* There is a corner case where the opener can spend so much that the
* non-opener can't add any non-dust HTLCs (since the opener would
@ -497,12 +529,14 @@ static enum channel_add_err add_htlc(struct channel *channel,
bool err_immediate_failures)
{
struct htlc *htlc, *old;
struct amount_msat msat_in_htlcs, committed_msat, adding_msat, removing_msat;
struct amount_msat msat_in_htlcs, committed_msat,
adding_msat, removing_msat, htlc_dust_amt;
enum side sender = htlc_state_owner(state), recipient = !sender;
const struct htlc **committed, **adding, **removing;
const struct channel_view *view;
size_t htlc_count;
bool option_anchor_outputs = channel_has(channel, OPT_ANCHOR_OUTPUTS);
u32 feerate, feerate_ceil;
htlc = tal(tmpctx, struct htlc);
@ -753,6 +787,42 @@ static enum channel_add_err add_htlc(struct channel *channel,
}
}
htlc_dust_amt = AMOUNT_MSAT(0);
feerate = channel_feerate(channel, recipient);
/* Note that we check for trimmed htlcs at an
* *accelerated* rate, so that future feerate changes
* don't suddenly surprise us */
feerate_ceil = htlc_trim_feerate_ceiling(feerate);
if (!htlc_dust(channel, committed,
adding, removing, recipient,
feerate_ceil, &htlc_dust_amt))
return CHANNEL_ERR_CHANNEL_CAPACITY_EXCEEDED;
if (amount_msat_greater(htlc_dust_amt,
channel->config[LOCAL].max_dust_htlc_exposure_msat)) {
htlc->fail_immediate = true;
if (err_immediate_failures)
return CHANNEL_ERR_DUST_FAILURE;
}
/* Also check the sender, as they'll eventually have the same
* constraint */
htlc_dust_amt = AMOUNT_MSAT(0);
feerate = channel_feerate(channel, sender);
feerate_ceil = htlc_trim_feerate_ceiling(feerate);
if (!htlc_dust(channel, committed, adding,
removing, sender, feerate_ceil,
&htlc_dust_amt))
return CHANNEL_ERR_CHANNEL_CAPACITY_EXCEEDED;
if (amount_msat_greater(htlc_dust_amt,
channel->config[LOCAL].max_dust_htlc_exposure_msat)) {
htlc->fail_immediate = true;
if (err_immediate_failures)
return CHANNEL_ERR_DUST_FAILURE;
}
dump_htlc(htlc, "NEW:");
htlc_map_add(channel->htlcs, tal_steal(channel, htlc));
if (htlcp)
@ -1109,6 +1179,37 @@ u32 approx_max_feerate(const struct channel *channel)
return avail.satoshis / weight * 1000; /* Raw: once-off reverse feerate*/
}
/* Is the sum of trimmed htlcs, as this new feerate, above our
* max allowed htlc dust limit? */
static struct amount_msat htlc_calculate_dust(const struct channel *channel,
u32 feerate_per_kw,
enum side side)
{
const struct htlc **committed, **adding, **removing;
struct amount_msat acc_dust = AMOUNT_MSAT(0);
gather_htlcs(tmpctx, channel, side,
&committed, &removing, &adding);
htlc_dust(channel, committed, adding, removing,
side, feerate_per_kw, &acc_dust);
return acc_dust;
}
bool htlc_dust_ok(const struct channel *channel,
u32 feerate_per_kw,
enum side side)
{
struct amount_msat total_dusted;
total_dusted = htlc_calculate_dust(channel, feerate_per_kw, side);
return amount_msat_greater_eq(
channel->config[LOCAL].max_dust_htlc_exposure_msat,
total_dusted);
}
bool can_opener_afford_feerate(const struct channel *channel, u32 feerate_per_kw)
{
struct amount_sat needed, fee;
@ -1180,6 +1281,9 @@ bool channel_update_feerate(struct channel *channel, u32 feerate_per_kw)
if (!can_opener_afford_feerate(channel, feerate_per_kw))
return false;
if (!htlc_dust_ok(channel, feerate_per_kw, REMOTE))
return false;
status_debug("Setting %s feerate to %u",
side_to_str(!channel->opener), feerate_per_kw);

View file

@ -180,6 +180,18 @@ u32 approx_max_feerate(const struct channel *channel);
*/
bool can_opener_afford_feerate(const struct channel *channel, u32 feerate);
/**
* htlc_dust_ok: will this feerate keep our dusted htlc's beneath
* the updated feerate?
*
* @channel: The channel state
* @feerate_per_kw: new feerate to test ok'ness for
* @side: which side's htlcs to verify
*/
bool htlc_dust_ok(const struct channel *channel,
u32 feerate_per_kw,
enum side side);
/**
* channel_update_feerate: Change fee rate on non-opener side.
* @channel: The channel

View file

@ -20,6 +20,8 @@ enum channel_add_err {
CHANNEL_ERR_HTLC_BELOW_MINIMUM,
/* HTLC would push past max_accepted_htlcs */
CHANNEL_ERR_TOO_MANY_HTLCS,
/* HTLC would push dusted-htlcs above max_dust_htlc_exposure_msat */
CHANNEL_ERR_DUST_FAILURE,
};
enum channel_remove_err {

View file

@ -42,3 +42,15 @@ bool htlc_is_trimmed(enum side htlc_owner,
return true;
return amount_msat_less_sat(htlc_amount, htlc_min);
}
/* Minimum amount of headroom we should use for
* anticipated feerate adjustments */
#define HTLC_FEE_MIN_RANGE 2530
#define max(a, b) ((a) > (b) ? (a) : (b))
u32 htlc_trim_feerate_ceiling(u32 feerate_per_kw)
{
/* Add the greater of 1.25x or 2530 sat/kw */
return max(feerate_per_kw + feerate_per_kw / 4,
feerate_per_kw + HTLC_FEE_MIN_RANGE);
}

View file

@ -12,4 +12,7 @@ bool htlc_is_trimmed(enum side htlc_owner,
enum side side,
bool option_anchor_outputs);
/* Calculate the our htlc-trimming buffer feerate
* (max(25%, 10s/vbyte) above feerate_per_kw) */
u32 htlc_trim_feerate_ceiling(u32 feerate_per_kw);
#endif /* LIGHTNING_COMMON_HTLC_TRIM_H */