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
This commit is contained in:
@@ -52,7 +52,7 @@ from .interface import GracefulDisconnect
|
|||||||
from .lnrouter import fee_for_edge_msat
|
from .lnrouter import fee_for_edge_msat
|
||||||
from .json_db import StoredDict
|
from .json_db import StoredDict
|
||||||
from .invoices import PR_PAID
|
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
|
from .trampoline import decode_routing_info
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -738,7 +738,7 @@ class Peer(Logger):
|
|||||||
raise Exception('Cannot create public channels')
|
raise Exception('Cannot create public channels')
|
||||||
|
|
||||||
channel_flags = CF_ANNOUNCE_CHANNEL if public else 0
|
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
|
# we set a channel type for internal bookkeeping
|
||||||
open_channel_tlvs = {}
|
open_channel_tlvs = {}
|
||||||
assert self.their_features.supports(LnFeatures.OPTION_STATIC_REMOTEKEY_OPT)
|
assert self.their_features.supports(LnFeatures.OPTION_STATIC_REMOTEKEY_OPT)
|
||||||
@@ -2219,7 +2219,17 @@ class Peer(Logger):
|
|||||||
"""
|
"""
|
||||||
if not chan.can_send_ctx_updates():
|
if not chan.can_send_ctx_updates():
|
||||||
return
|
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 not chan.constraints.is_initiator:
|
||||||
if constants.net is not constants.BitcoinRegtest:
|
if constants.net is not constants.BitcoinRegtest:
|
||||||
chan_feerate = chan.get_latest_feerate(LOCAL)
|
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"({chan.get_id_for_log()}) feerate is {chan_feerate} sat/kw, "
|
||||||
f"current recommended feerate is {feerate_per_kw} sat/kw, consider force closing!")
|
f"current recommended feerate is {feerate_per_kw} sat/kw, consider force closing!")
|
||||||
return
|
return
|
||||||
|
# it is our responsibility to update the fee
|
||||||
chan_fee = chan.get_next_feerate(REMOTE)
|
chan_fee = chan.get_next_feerate(REMOTE)
|
||||||
if feerate_per_kw < chan_fee / 2:
|
if does_chan_fee_need_update(chan_fee):
|
||||||
self.logger.info("FEES HAVE FALLEN")
|
self.logger.info(f"({chan.get_id_for_log()}) onchain fees have changed considerably. updating fee.")
|
||||||
elif feerate_per_kw > chan_fee * 2:
|
|
||||||
self.logger.info("FEES HAVE RISEN")
|
|
||||||
elif chan.get_latest_ctn(REMOTE) == 0:
|
elif chan.get_latest_ctn(REMOTE) == 0:
|
||||||
# workaround eclair issue https://github.com/ACINQ/eclair/issues/1730
|
# 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:
|
if feerate_per_kw == chan_fee:
|
||||||
feerate_per_kw += 1
|
feerate_per_kw += 1
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -2914,7 +2914,7 @@ class LNWallet(LNWorker):
|
|||||||
else:
|
else:
|
||||||
await self.taskgroup.spawn(self.reestablish_peer_for_given_channel(chan))
|
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 FEE_LN_ETA_TARGET, FEERATE_FALLBACK_STATIC_FEE
|
||||||
from .simple_config import FEERATE_PER_KW_MIN_RELAY_LIGHTNING
|
from .simple_config import FEERATE_PER_KW_MIN_RELAY_LIGHTNING
|
||||||
if constants.net is constants.BitcoinRegtest:
|
if constants.net is constants.BitcoinRegtest:
|
||||||
@@ -2925,6 +2925,18 @@ class LNWallet(LNWorker):
|
|||||||
feerate_per_kvbyte = FEERATE_FALLBACK_STATIC_FEE
|
feerate_per_kvbyte = FEERATE_FALLBACK_STATIC_FEE
|
||||||
return max(FEERATE_PER_KW_MIN_RELAY_LIGHTNING, feerate_per_kvbyte // 4)
|
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):
|
def create_channel_backup(self, channel_id: bytes):
|
||||||
chan = self._channels[channel_id]
|
chan = self._channels[channel_id]
|
||||||
# do not backup old-style channels
|
# do not backup old-style channels
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ from .logging import get_logger, Logger
|
|||||||
FEE_ETA_TARGETS = [25, 10, 5, 2]
|
FEE_ETA_TARGETS = [25, 10, 5, 2]
|
||||||
FEE_DEPTH_TARGETS = [10_000_000, 5_000_000, 2_000_000, 1_000_000,
|
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]
|
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
|
# satoshi per kbyte
|
||||||
FEERATE_MAX_DYNAMIC = 1500000
|
FEERATE_MAX_DYNAMIC = 1500000
|
||||||
|
|||||||
Reference in New Issue
Block a user