Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add htlc dust ceiling #4837

Merged
merged 9 commits into from
Oct 23, 2021
42 changes: 40 additions & 2 deletions channeld/channeld.c
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,11 @@ static void handle_peer_add_htlc(struct peer *peer, const u8 *msg)
#endif
add_err = channel_add_htlc(peer->channel, REMOTE, id, amount,
cltv_expiry, &payment_hash,
onion_routing_packet, blinding, &htlc, NULL);
onion_routing_packet, blinding, &htlc, NULL,
/* We don't immediately fail incoming htlcs,
* instead we wait and fail them after
* they've been committed */
false);
if (add_err != CHANNEL_ERR_ADD_OK)
peer_failed_warn(peer->pps, &peer->channel_id,
"Bad peer_add_htlc: %s",
Expand Down Expand Up @@ -1288,6 +1292,28 @@ static void send_commit(struct peer *peer)
if (feerate_changes_done(peer->channel->fee_states, false)) {
u8 *msg;

/* BOLT-919 #2:
*
* A sending node:
* - if the `dust_balance_on_counterparty_tx` at the
* new `dust_buffer_feerate` is superior to
* `max_dust_htlc_exposure_msat`:
* - MAY NOT send `update_fee`
* - MAY fail the channel
* - if the `dust_balance_on_holder_tx` at the
* new `dust_buffer_feerate` is superior to
* the `max_dust_htlc_exposure_msat`:
* - MAY NOT send `update_fee`
* - MAY fail the channel
*/
/* 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))
peer_failed_warn(peer->pps, &peer->channel_id,
"Too much dust to update fee (Desired"
" feerate update %d)", feerate_target);

if (!channel_update_feerate(peer->channel, feerate_target))
status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Could not afford feerate %u"
Expand Down Expand Up @@ -1468,6 +1494,7 @@ static void marshall_htlc_info(const tal_t *ctx,
ecdh(a.blinding, &a.blinding_ss);
} else
a.blinding = NULL;
a.fail_immediate = htlc->fail_immediate;
tal_arr_expand(added, a);
} else if (htlc->state == RCVD_REMOVE_COMMIT) {
if (htlc->r) {
Expand Down Expand Up @@ -3299,7 +3326,8 @@ static void handle_offer_htlc(struct peer *peer, const u8 *inmsg)

e = channel_add_htlc(peer->channel, LOCAL, peer->htlc_id,
amount, cltv_expiry, &payment_hash,
onion_routing_packet, take(blinding), NULL, &htlc_fee);
onion_routing_packet, take(blinding), NULL,
&htlc_fee, true);
status_debug("Adding HTLC %"PRIu64" amount=%s cltv=%u gave %s",
peer->htlc_id,
type_to_string(tmpctx, struct amount_msat, &amount),
Expand Down Expand Up @@ -3354,6 +3382,16 @@ 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:
/* BOLT-919 #2:
* - upon an outgoing HTLC:
* - if a HTLC's `amount_msat` is inferior the counterparty's...
* - SHOULD NOT send this HTLC
* - SHOULD fail this HTLC if it's forwarded
*/
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();
Expand Down
3 changes: 3 additions & 0 deletions channeld/channeld_htlc.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ struct htlc {

/* Blinding (optional). */
struct pubkey *blinding;

/* Should we immediately fail this htlc? */
bool fail_immediate;
};

static inline bool htlc_has(const struct htlc *h, int flag)
Expand Down
16 changes: 16 additions & 0 deletions channeld/commit_tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
20 changes: 20 additions & 0 deletions channeld/commit_tx.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
137 changes: 132 additions & 5 deletions channeld/full_channel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -493,21 +525,25 @@ static enum channel_add_err add_htlc(struct channel *channel,
const struct pubkey *blinding TAKES,
struct htlc **htlcp,
bool enforce_aggregate_limits,
struct amount_sat *htlc_fee)
struct amount_sat *htlc_fee,
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);

htlc->id = id;
htlc->amount = amount;
htlc->state = state;
htlc->fail_immediate = false;

htlc->rhash = *payment_hash;
if (blinding)
Expand Down Expand Up @@ -751,6 +787,54 @@ 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)) {
/* BOLT-919 #2:
* A node:
* - upon an incoming HTLC:
* - if a HTLC's `amount_msat` is inferior to the
* counterparty's `dust_limit_satoshis` plus the HTLC-timeout fee
* at the `dust_buffer_feerate`: ...
* - SHOULD fail this HTLC once it's committed
* - SHOULD NOT reveal a preimage for this HTLC
*/
/* Note: Marking this as 'fail_immediate' and
* NOT returning an ERR will fail this HTLC
* once it's committed */
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)
Expand All @@ -768,7 +852,8 @@ enum channel_add_err channel_add_htlc(struct channel *channel,
const u8 routing[TOTAL_PACKET_SIZE(ROUTING_INFO_SIZE)],
const struct pubkey *blinding TAKES,
struct htlc **htlcp,
struct amount_sat *htlc_fee)
struct amount_sat *htlc_fee,
bool err_immediate_failures)
{
enum htlc_state state;

Expand All @@ -786,7 +871,7 @@ enum channel_add_err channel_add_htlc(struct channel *channel,

return add_htlc(channel, state, id, amount, cltv_expiry,
payment_hash, routing, blinding,
htlcp, true, htlc_fee);
htlcp, true, htlc_fee, err_immediate_failures);
}

struct htlc *channel_get_htlc(struct channel *channel, enum side sender, u64 id)
Expand Down Expand Up @@ -1106,6 +1191,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;
Expand Down Expand Up @@ -1177,6 +1293,17 @@ bool channel_update_feerate(struct channel *channel, u32 feerate_per_kw)
if (!can_opener_afford_feerate(channel, feerate_per_kw))
return false;

/* BOLT-919 #2:
* - if the `dust_balance_on_holder_tx` at the
* new `dust_buffer_feerate` is superior to
* the `max_dust_htlc_exposure_msat`:
* ...
* - MAY fail the channel
*/
if (!htlc_dust_ok(channel, feerate_per_kw, REMOTE) ||
!htlc_dust_ok(channel, feerate_per_kw, LOCAL))
return false;

status_debug("Setting %s feerate to %u",
side_to_str(!channel->opener), feerate_per_kw);

Expand Down Expand Up @@ -1392,7 +1519,7 @@ bool channel_force_htlcs(struct channel *channel,
&htlcs[i]->payment_hash,
htlcs[i]->onion_routing_packet,
htlcs[i]->blinding,
&htlc, false, NULL);
&htlc, false, NULL, false);
if (e != CHANNEL_ERR_ADD_OK) {
status_broken("%s HTLC %"PRIu64" failed error %u",
htlc_state_owner(htlcs[i]->state) == LOCAL
Expand Down
19 changes: 18 additions & 1 deletion channeld/full_channel.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ u32 actual_feerate(const struct channel *channel,
* @routing: routing information (copied)
* @blinding: optional blinding information for this HTLC.
* @htlcp: optional pointer for resulting htlc: filled in if and only if CHANNEL_ERR_NONE.
* @err_immediate_failures: in some cases (dusty htlcs) we want to immediately
* fail the htlc; for peer incoming don't want to
* error, but rather mark it as failed and fail after
* it's been committed to (so set this to false)
*
* If this returns CHANNEL_ERR_NONE, the fee htlc was added and
* the output amounts adjusted accordingly. Otherwise nothing
Expand All @@ -117,7 +121,8 @@ enum channel_add_err channel_add_htlc(struct channel *channel,
const u8 routing[TOTAL_PACKET_SIZE(ROUTING_INFO_SIZE)],
const struct pubkey *blinding TAKES,
struct htlc **htlcp,
struct amount_sat *htlc_fee);
struct amount_sat *htlc_fee,
bool err_immediate_failures);

/**
* channel_get_htlc: find an HTLC
Expand Down Expand Up @@ -175,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
Expand Down
2 changes: 2 additions & 0 deletions channeld/full_channel_error.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading