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 new config setting max_balance_dust_htlc_msat #1009

Merged

Conversation

ariard
Copy link

@ariard ariard commented Jul 20, 2021

First step to support lightning/bolts#873

@ariard ariard added this to the 0.0.100 milestone Jul 20, 2021
@ariard ariard marked this pull request as draft July 20, 2021 01:37
@@ -228,6 +236,7 @@ impl_writeable_tlv_based!(ChannelConfig, {
(4, announced_channel, required),
(6, commit_upfront_shutdown_pubkey, required),
(8, forwarding_fee_base_msat, required),
(10, max_outbound_dusted_htlc_msat, required),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making this required means we cannot read serialized configs from old versions. Can you take https://git.bitcoin.ninja/index.cgi?p=rust-lightning;a=commit;h=fcf9ac5ca6055254955fca9a1402b24168aeab14 and use (default_value, 25_350_000) instead of required?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it present another burden for the user, namely to override the default value after deser if user is willingly to enforce its own setting ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we'll need them to update the field after loading. Sadly we currently don't have any mechanism to update channel config after load at all, but it's something we'll need to as soon to allow updating channel channel relay fees soon.

/// than counterparty's `dust_limit_satoshis` sumed up with their expexted onchain claim
/// cost at minimal mempools congestion.
///
/// Default value: 25_350_000 (330 sat + `min_feerate_per_kw` * HTLC_SUCCESS_TX_WEIGHT / 1000) * 50 * 1000
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's any reason to keep the current default here. Maybe 10k sats for max exposure of $5? Maybe 5k if we want to go a little lower?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Picked up 5k as we might expect this threshold to become more sensible with time for the average node operator.

@ariard ariard force-pushed the 2021-07-add-forward-dust-limit branch 2 times, most recently from 44e3137 to cf0c5c9 Compare July 20, 2021 22:23
@ariard
Copy link
Author

ariard commented Jul 20, 2021

New default is breaking tests, gonna fix that.

@TheBlueMatt
Copy link
Collaborator

You can also simply override the default to a higher value in the default config constructor in functional_test_utils.

@ariard
Copy link
Author

ariard commented Jul 22, 2021

Beyond fixes, add a supplemental commit to generalize the new setting on incoming dust HTLC on counterparty commitment transaction. I'm still toying with the idea of introducing a new max_accepted_dust_htlc to achieve exposure symmetry on incoming dust HTLC on holder commitment transaction, currently limited (and as such dependent) by max_accepted_htlc.

@codecov
Copy link

codecov bot commented Jul 22, 2021

Codecov Report

Merging #1009 (d282586) into main (69ee486) will increase coverage by 1.71%.
The diff coverage is 98.03%.

❗ Current head d282586 differs from pull request most recent head 730f6f3. Consider uploading reports for the commit 730f6f3 to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1009      +/-   ##
==========================================
+ Coverage   90.79%   92.50%   +1.71%     
==========================================
  Files          61       63       +2     
  Lines       31578    37857    +6279     
==========================================
+ Hits        28672    35021    +6349     
+ Misses       2906     2836      -70     
Impacted Files Coverage Δ
lightning/src/util/ser_macros.rs 96.31% <ø> (ø)
lightning/src/util/config.rs 46.51% <33.33%> (+0.17%) ⬆️
lightning/src/ln/channel.rs 93.04% <97.84%> (+3.80%) ⬆️
lightning/src/ln/functional_test_utils.rs 95.53% <100.00%> (+0.35%) ⬆️
lightning/src/ln/functional_tests.rs 98.98% <100.00%> (+1.69%) ⬆️
lightning/src/util/errors.rs 69.33% <0.00%> (-2.10%) ⬇️
lightning-background-processor/src/lib.rs 95.22% <0.00%> (-0.36%) ⬇️
lightning/src/ln/script.rs 93.75% <0.00%> (ø)
lightning/src/ln/monitor_tests.rs 100.00% <0.00%> (ø)
lightning/src/ln/chanmon_update_fail_tests.rs 97.80% <0.00%> (+0.05%) ⬆️
... and 9 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 69ee486...730f6f3. Read the comment docs.

lightning/src/util/config.rs Outdated Show resolved Hide resolved
lightning/src/ln/channel.rs Outdated Show resolved Hide resolved
@@ -2032,6 +2047,11 @@ impl<Signer: Sign> Channel<Signer> {
}
}

if inbound_dusted_htlc_msat + msg.amount_msat > self.get_max_balance_dust_htlc_msat() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand why we need to enforce this on the inbound leg. It should be sufficient to enforce it only on outbound HTLCs - ensuring our balance isn't dusted heavily. If our counterparty wants to burn all their balance to dust, that's on them.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ariard pointed out that this matters if we have to go claim the funds from this HTLC after it was forwarded onwards. The funds may be our counterparty's balance, but once we receive the preimage from the place we forwarded the HTLC to, the funds are now "ours" and may not be in the commitment transaction. Thus, this does need to be here.

lightning/src/util/config.rs Outdated Show resolved Hide resolved
/// Applied on outgoing dust HTLCs and incoming dust HTLCs committed on the counterparty
/// commitment transaction.
///
/// Dust HTLCs are defined as any HTLC lower than counterparty's `dust_limit_satoshis` summed
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Users have no idea what dust_limit_satoshis is, or why it matters at all what the sum of dust HTLCs is. We need to say something that describes, without assuming any significant knowledge of lightning, why we limit the dust balance to some low value (that those funds go to miners, and that a malicious miner could forward lots of small payments through us that miners claim as additional fee revenue if we force-close).

@TheBlueMatt TheBlueMatt self-requested a review July 22, 2021 03:39
@TheBlueMatt
Copy link
Collaborator

Oops, the "approve" check shouldn't have been marked on that one. I mean Concept ACK, but I left comments :).

@ariard ariard force-pushed the 2021-07-add-forward-dust-limit branch from 0db41f0 to 3faf229 Compare July 29, 2021 00:06
@ariard ariard changed the title Add new config setting max_outbound_dusted_htlc_msat Add new config setting max_balance_dust_htlc_msat Jul 29, 2021
}
}

if msg.amount_msat <= (self.feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) * 1000 + self.holder_dust_limit_satoshis {
Copy link
Author

@ariard ariard Jul 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went through a lot of back-and-forth on the utility to also check the dust balance on the holder transaction as it's implicitly protected by the check on the commitment transaction, with our current assumption that holder's dust_limit_satoshis is always under counterparty's one.

Though finally I left it, as it gives us more belt-and-suspender if we touch our config settings/implementation default in the future.

Without, as of today, the worst-fee discrepancy would be 344_000 msat as explained in this Big Comment(tm): https://github.com/ariard/rust-lightning/blob/0db41f057ac4967f72646cc1e77ee59280fbf07c/lightning/src/ln/channel.rs#L2066

@ariard ariard marked this pull request as ready for review July 29, 2021 00:14
@ariard
Copy link
Author

ariard commented Jul 29, 2021

See lastest squashed branch at 3faf229, addressing review comment and hopefully exhaustive test coverage!

@ariard ariard force-pushed the 2021-07-add-forward-dust-limit branch from 3faf229 to 5158a1c Compare July 30, 2021 22:50
@TheBlueMatt
Copy link
Collaborator

Needs rebase.

Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Few doc comments and one actual code comment, haven't reviewed the test yet.

lightning/src/util/config.rs Show resolved Hide resolved
lightning/src/ln/channel.rs Outdated Show resolved Hide resolved
lightning/src/ln/channel.rs Outdated Show resolved Hide resolved
@@ -2032,6 +2061,20 @@ impl<Signer: Sign> Channel<Signer> {
}
}

if msg.amount_msat / 1000 < (self.feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we agree to a multiplier here? Ie instead of checking with the current feerate, check with the current feerate times 2/4/whatever to prevent feerate increases from blowing up our dust? I think we should set the multiplier pretty high so that we can remove the counterparty feerate check entirely and only ever check the dust threshold.

Copy link
Author

@ariard ariard Aug 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

edit: I think we agree in principle, but not on the heuristics to pick up an adequate feerate. In fact I'm not sure it makes sense to take a multiplier against the current feerate as this one might be already in the higher percentiles of the mempool feerate groups, thus allowing this HTLC to consume all our dust bandwidth.

Maybe we should instead give us a multiplier of the dust balance against a fixed point of 253 sat/kWU ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, the reason I like a multiplier is that, without any other information, if the feerate is high, it seems likely the feerate may increase significantly. If the feerate is low, probably it will only increase by 10sat/vb or so.

We could do something nonlinear, but I'm not a fan of a straight constant. Maybe we could always increase by max(10 sat/vb, 0.25x current feerate)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay so implemented the evaluation against a preventive high-feerate, note tests coverage has been modified in consequence to avoid the newly inflated check of counterparty dust limit satoshi blocking failure against the holder limit (508_000 -> 2_300_000)

                let pending_remote_value_msat =
@@ -3482,6 +3496,10 @@ impl<Signer: Sign> Channel<Signer> {
                self.feerate_per_kw
        }
 
+       pub fn get_dust_buffer_feerate(&self) -> u32 {
+               cmp::max(2530, self.feerate_per_kw * 1250 / 1000)
+       }
+

Comment on lines 210 to 220
/// Limit holder balance exposure to dusted HTLCs at any given time.
///
/// Applied on at HTLC reception/forwarding, *each* on both-sides commitment transactions.
///
/// Dust HTLCs are defined as any HTLC lower than counterparty's `dust_limit_satoshis` or
/// holder's `dust_limit_satoshis` summed up with their expected onchain claim cost at
/// negotiated feerate (`feerate_per_kw` * HTLC_*_TX_WEIGHT * 1000 / 1000).
///
/// This limit does exist to prevent holder balance burnt to miners fees if the link is
/// broken, either due to a accidental force-closure or a counterparty maliciously
/// cooperating with miners.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This includes way too much technical detail about the implementation and not enough detail about what this means for users.

Suggested change
/// Limit holder balance exposure to dusted HTLCs at any given time.
///
/// Applied on at HTLC reception/forwarding, *each* on both-sides commitment transactions.
///
/// Dust HTLCs are defined as any HTLC lower than counterparty's `dust_limit_satoshis` or
/// holder's `dust_limit_satoshis` summed up with their expected onchain claim cost at
/// negotiated feerate (`feerate_per_kw` * HTLC_*_TX_WEIGHT * 1000 / 1000).
///
/// This limit does exist to prevent holder balance burnt to miners fees if the link is
/// broken, either due to a accidental force-closure or a counterparty maliciously
/// cooperating with miners.
/// Limit our total exposure to in-flight HTLCs which are burned to fees as they are too small.
/// to claim on-chain.
///
/// When an HTLC present in one of our channels is below a "dust" threshold, the HTLC will not
/// be claimable on-chain, instead being turned into additional miner fees if either party
/// force-closes the channel. Because the threshold is per-HTLC, our total exposure to such
/// payments may be substantial if there are many dust HTLCs present when the channel is
/// force-closed.
///
/// This limit is applied for send, forwarded, and received HTLCs and limits the total exposure
/// across all three types per-channel. Setting this too low may prevent the sending or receipt
/// of low-value HTLCs on high-traffic nodes, and is very important if your counterparty is a
/// miner (as they could benefit from such dust HTLCs becoming miner fees).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better, though I would recall the last part about counterparty=miner being a worrying concern is kind of helpless. Even assuming you can effectively identify your LN counterparty, you can't exclude unseen out-of-band negotiation with a miner pool to introduce such non-propagated "dust-stealing tx".

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I think you're right. It would give users too much of a false sense of security.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep modified a bit.

@ariard ariard force-pushed the 2021-07-add-forward-dust-limit branch 2 times, most recently from ec96b78 to ad611ab Compare August 5, 2021 16:31
This allows TLV serialization macros to read non-Option-wrapped
types but allow them to be missing, filling them in with the
provided default value as needed.
@ariard ariard force-pushed the 2021-07-add-forward-dust-limit branch from ad611ab to 6876991 Compare August 5, 2021 16:36
Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No functional issues, only docs/logs/git issues.

lightning/src/ln/channel.rs Show resolved Hide resolved
lightning/src/ln/channel.rs Show resolved Hide resolved
lightning/src/ln/channel.rs Outdated Show resolved Hide resolved
lightning/src/util/config.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@valentinewallace valentinewallace left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly nits, overall, this makes a lot of sense to me as a precursor to lightning/bolts#873.

Still grokking but I do wonder if the dust_buffer_feerate deserves more thought/is more suited to its own PR, like should we also use it in dust calculations like in next_remote_commit_tx_fee_msat?

lightning/src/util/config.rs Outdated Show resolved Hide resolved
lightning/src/util/config.rs Outdated Show resolved Hide resolved
/// An enum gathering stats on pending HTLCs, either inbound or outbound side.
struct HTLCStats {
pending_htlcs: u32,
pending_htlcs_value_msat: u64,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggested nit: s/value/balance

Copy link
Author

@ariard ariard Aug 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think balance includes either the to_local or to_remote output so would like to avoid blurring meanings here.

Comment on lines 281 to 282
on_counterparty_tx_dust_balance: u64,
on_holder_tx_dust_balance: u64,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: get rid of the on_ prefixes, and add _msat at the end

Copy link
Author

@ariard ariard Aug 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to on_holder_tx_dust_exposure_msat/on_counterparty_tx_dust_exposure_msat. I concede the on_ prefix can be redundant but it's easy for a reviewer to interpret dust_balance as coming from holder channel policy imo

lightning/src/ln/channel.rs Show resolved Hide resolved
@@ -218,6 +234,7 @@ impl Default for ChannelConfig {
cltv_expiry_delta: 6 * 12, // 6 blocks/hour * 12 hours
announced_channel: false,
commit_upfront_shutdown_pubkey: true,
max_balance_dust_htlc_msat: 5_000_000,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make a const of this number and use it everywhere?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well the interest of a configuration setting, if a channel is opened with a trusted/highly-reliable peer, this number could be considerably lifted up and as such increase the dust bandwidth available on this link. The reasoning holds also in the other sense, a LDK node operator could consider the number picked up by us too high in function of their economic capabilities and would like to reduce.

@@ -2139,6 +2139,20 @@ impl<Signer: Sign> Channel<Signer> {
}
}

if msg.amount_msat / 1000 < (self.get_dust_buffer_feerate() as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: prefer making (self.get_dust_buffer_feerate() as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis into a variable and below

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean in this function call ? I think it's used only once, as the following check is based on HTLC_SUCCESS_TX_WEIGHT?

lightning/src/ln/channel.rs Outdated Show resolved Hide resolved
lightning/src/ln/functional_tests.rs Outdated Show resolved Hide resolved
@@ -2098,8 +2139,22 @@ impl<Signer: Sign> Channel<Signer> {
}
}

if msg.amount_msat / 1000 < (self.get_dust_buffer_feerate() as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis {
if inbound_stats.on_counterparty_tx_dust_balance + outbound_stats.on_counterparty_tx_dust_balance + msg.amount_msat > self.get_max_balance_dust_htlc_msat() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: why not make get_max_balance_dust_htlc_msat include transaction fees for paying for said HTLCs, and instead check that the total dust HTLC values + their fees don't violate max_balance_dust_htlc_msat? Not fully thought out but to me that seems like it would have more protection against burning $ to miners

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if understand well your point but this check does include the transaction fees potentially paying for said HTLCs with the following equation component : (self.get_dust_buffer_feerate() as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) ? It's just we use a preventive higher-feerate to anticipate fees spikes during the lifetime of the HTLC on the commitment transactions.

@ariard ariard force-pushed the 2021-07-add-forward-dust-limit branch from 6876991 to 5eb6cce Compare August 10, 2021 11:54
@ariard
Copy link
Author

ariard commented Aug 10, 2021

Updated at 5eb6cce, incorporating lastest reviews.

@ariard
Copy link
Author

ariard commented Aug 10, 2021

Still grokking but I do wonder if the dust_buffer_feerate deserves more thought/is more suited to its own PR, like should we also use it in dust calculations like in next_remote_commit_tx_fee_msat?

I don't think so, this is a newer "dust" definition whereas the one in next_remote_commit_tx_fee_msat spec-level one. The former serves to protect us against dust HTLC exposure, with a margin of safety (get_dust_buffer_feerate) and the latter serves to agree among implementations on non-economically interesting HTLCs to claim on-chain and better to prune out.

Copy link
Contributor

@valentinewallace valentinewallace left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically LGTM. Would be nice to fix fuzz

lightning/src/util/config.rs Outdated Show resolved Hide resolved
lightning/src/ln/functional_test_utils.rs Outdated Show resolved Hide resolved
lightning/src/ln/channel.rs Outdated Show resolved Hide resolved
/// This limit is applied for sent, forwarded, and received HTLCs and limits the total
/// exposure across all three types per-channel. Setting this too low may prevent the
/// sending or receipt of low-value HTLCs on high-traffic nodes, and this limit is very
/// important to prevent stealing of dust HTLCs by miners.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Stealing" might sound a little intense to users. Is there another way of phrasing it? lol

@TheBlueMatt
Copy link
Collaborator

This should fix the fuzz test:

$ git diff
diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs
index 70ddac5d..36d12ad5 100644
--- a/fuzz/src/chanmon_consistency.rs
+++ b/fuzz/src/chanmon_consistency.rs
@@ -244,6 +244,7 @@ fn check_api_err(api_err: APIError) {
                                _ if err.starts_with("Cannot send value that would put counterparty balance under holder-announced channel reserve value") => {},
                                _ if err.starts_with("Cannot send value that would overdraw remaining funds.") => {},
                                _ if err.starts_with("Cannot send value that would not leave enough to pay for fees.") => {},
+                               _ if err.starts_with("Cannot send value that would put our exposure to dust HTLCs at") => {},
                                _ => panic!("{}", err),
                        }
                },

Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM aside from one comment and the fuzz failure I pasted a patch for.

lightning/src/ln/channel.rs Outdated Show resolved Hide resolved
Antoine Riard added 2 commits August 10, 2021 13:43
Trimmed-to-dust HTLCs are at risk of being burnt as miner fees
at anytime during their lifetime due to the broadcast of either
holder commitment transaction or counterparty's one.

To hedge against this risk, we introduce a new config setting
`max_balance_dust_htlc_msat`, with the initial value of
5_000_000 msat.
@ariard ariard force-pushed the 2021-07-add-forward-dust-limit branch from 5eb6cce to d282586 Compare August 10, 2021 17:52
Antoine Riard added 2 commits August 10, 2021 17:30
At `update_add_htlc()`/`send_htlc()`, we verify that the inbound/
outbound dust or the sum of both, on either sides of the link isn't
above new config setting `max_balance_dust_htlc_msat`.

A dust HTLC is hence defined as a trimmed-to-dust one, i.e including
the fee cost to publish its claiming transaction.
@ariard ariard force-pushed the 2021-07-add-forward-dust-limit branch from d282586 to 730f6f3 Compare August 10, 2021 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants