From e6a0455ced2bf5f2e654ec79401107bbc9a4296c Mon Sep 17 00:00:00 2001 From: SomberNight Date: Wed, 31 Jan 2024 08:47:29 +0000 Subject: [PATCH] lnpeer: raise chan fees using update_fee more aggressively The existing logic of only updating the fee if it is not within 2x of the current 2-block-eta does not work well for the current mempool. The current mempool looks a bit weird: you need ~20 sat/vbyte to even get into it, but there is only around 5 MB of txs paying >25 sat/vbyte. The estimates look like this: ``` >>> config.fee_estimates {144: 25764, 25: 27075, 10: 34538, 5: 34538, 2: 34538} ``` This commit changes the logic so that we send update_fee if the old rate is - below 75% of the current 2-block-eta (instead of 50%), or - below the 25-block-eta --- electrum/lnpeer.py | 25 +++++++++++++++++-------- electrum/lnworker.py | 14 +++++++++++++- electrum/simple_config.py | 3 ++- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py index 537eb292c..e4138c1a5 100644 --- a/electrum/lnpeer.py +++ b/electrum/lnpeer.py @@ -52,7 +52,7 @@ from .interface import GracefulDisconnect from .lnrouter import fee_for_edge_msat from .json_db import StoredDict from .invoices import PR_PAID -from .simple_config import FEE_LN_ETA_TARGET +from .simple_config import FEE_LN_ETA_TARGET, FEERATE_PER_KW_MIN_RELAY_LIGHTNING from .trampoline import decode_routing_info if TYPE_CHECKING: @@ -738,7 +738,7 @@ class Peer(Logger): raise Exception('Cannot create public channels') channel_flags = CF_ANNOUNCE_CHANNEL if public else 0 - feerate = self.lnworker.current_feerate_per_kw() + feerate = self.lnworker.current_target_feerate_per_kw() # we set a channel type for internal bookkeeping open_channel_tlvs = {} assert self.their_features.supports(LnFeatures.OPTION_STATIC_REMOTEKEY_OPT) @@ -2219,7 +2219,17 @@ class Peer(Logger): """ if not chan.can_send_ctx_updates(): return - feerate_per_kw = self.lnworker.current_feerate_per_kw() + feerate_per_kw = self.lnworker.current_target_feerate_per_kw() + def does_chan_fee_need_update(chan_feerate: Union[float, int]) -> bool: + # We raise fees more aggressively than we lower them. Overpaying is not too bad, + # but lowballing can be fatal if we can't even get into the mempool... + high_fee = 2 * feerate_per_kw # type: Union[float, int] + low_fee = self.lnworker.current_low_feerate_per_kw() # type: Union[float, int] + low_fee = max(low_fee, 0.75 * feerate_per_kw) + # make sure low_feerate and target_feerate are not too close to each other: + low_fee = min(low_fee, feerate_per_kw - FEERATE_PER_KW_MIN_RELAY_LIGHTNING) + assert low_fee < high_fee, (low_fee, high_fee) + return not (low_fee < chan_feerate < high_fee) if not chan.constraints.is_initiator: if constants.net is not constants.BitcoinRegtest: chan_feerate = chan.get_latest_feerate(LOCAL) @@ -2232,14 +2242,13 @@ class Peer(Logger): f"({chan.get_id_for_log()}) feerate is {chan_feerate} sat/kw, " f"current recommended feerate is {feerate_per_kw} sat/kw, consider force closing!") return + # it is our responsibility to update the fee chan_fee = chan.get_next_feerate(REMOTE) - if feerate_per_kw < chan_fee / 2: - self.logger.info("FEES HAVE FALLEN") - elif feerate_per_kw > chan_fee * 2: - self.logger.info("FEES HAVE RISEN") + if does_chan_fee_need_update(chan_fee): + self.logger.info(f"({chan.get_id_for_log()}) onchain fees have changed considerably. updating fee.") elif chan.get_latest_ctn(REMOTE) == 0: # workaround eclair issue https://github.com/ACINQ/eclair/issues/1730 - self.logger.info("updating fee to bump remote ctn") + self.logger.info(f"({chan.get_id_for_log()}) updating fee to bump remote ctn") if feerate_per_kw == chan_fee: feerate_per_kw += 1 else: diff --git a/electrum/lnworker.py b/electrum/lnworker.py index adee95059..8b1353374 100644 --- a/electrum/lnworker.py +++ b/electrum/lnworker.py @@ -2914,7 +2914,7 @@ class LNWallet(LNWorker): else: await self.taskgroup.spawn(self.reestablish_peer_for_given_channel(chan)) - def current_feerate_per_kw(self): + def current_target_feerate_per_kw(self) -> int: from .simple_config import FEE_LN_ETA_TARGET, FEERATE_FALLBACK_STATIC_FEE from .simple_config import FEERATE_PER_KW_MIN_RELAY_LIGHTNING if constants.net is constants.BitcoinRegtest: @@ -2925,6 +2925,18 @@ class LNWallet(LNWorker): feerate_per_kvbyte = FEERATE_FALLBACK_STATIC_FEE return max(FEERATE_PER_KW_MIN_RELAY_LIGHTNING, feerate_per_kvbyte // 4) + def current_low_feerate_per_kw(self) -> int: + from .simple_config import FEE_LN_LOW_ETA_TARGET + from .simple_config import FEERATE_PER_KW_MIN_RELAY_LIGHTNING + if constants.net is constants.BitcoinRegtest: + feerate_per_kvbyte = 0 + else: + feerate_per_kvbyte = self.network.config.eta_target_to_fee(FEE_LN_LOW_ETA_TARGET) or 0 + low_feerate_per_kw = max(FEERATE_PER_KW_MIN_RELAY_LIGHTNING, feerate_per_kvbyte // 4) + # make sure this is never higher than the target feerate: + low_feerate_per_kw = min(low_feerate_per_kw, self.current_target_feerate_per_kw()) + return low_feerate_per_kw + def create_channel_backup(self, channel_id: bytes): chan = self._channels[channel_id] # do not backup old-style channels diff --git a/electrum/simple_config.py b/electrum/simple_config.py index ed5f25ec8..16ba28fa5 100644 --- a/electrum/simple_config.py +++ b/electrum/simple_config.py @@ -24,7 +24,8 @@ from .logging import get_logger, Logger FEE_ETA_TARGETS = [25, 10, 5, 2] FEE_DEPTH_TARGETS = [10_000_000, 5_000_000, 2_000_000, 1_000_000, 800_000, 600_000, 400_000, 250_000, 100_000] -FEE_LN_ETA_TARGET = 2 # note: make sure the network is asking for estimates for this target +FEE_LN_ETA_TARGET = 2 # note: make sure the network is asking for estimates for this target +FEE_LN_LOW_ETA_TARGET = 25 # note: make sure the network is asking for estimates for this target # satoshi per kbyte FEERATE_MAX_DYNAMIC = 1500000