1
0

don't use fallback feerates in lightning by default

This commit is contained in:
f321x
2025-04-01 12:58:57 +02:00
parent bf317af30b
commit 0b19b660c5
3 changed files with 36 additions and 11 deletions

View File

@@ -23,8 +23,8 @@ from .crypto import sha256, sha256d, privkey_to_pubkey
from . import bitcoin, util
from . import constants
from .util import (bfh, log_exceptions, ignore_exceptions, chunks, OldTaskGroup,
UnrelatedTransactionException, error_text_bytes_to_safe_str, AsyncHangDetector)
from .util import event_listener, EventListener
UnrelatedTransactionException, error_text_bytes_to_safe_str, AsyncHangDetector,
NoDynamicFeeEstimates, event_listener, EventListener)
from . import transaction
from .bitcoin import make_op_return, DummyAddress
from .transaction import PartialTxOutput, match_script_against_template, Sighash
@@ -993,7 +993,9 @@ class Peer(Logger, EventListener):
raise Exception('Not a trampoline node: ' + str(self.their_features))
channel_flags = CF_ANNOUNCE_CHANNEL if public else 0
feerate = self.lnworker.current_target_feerate_per_kw()
feerate: Optional[int] = self.lnworker.current_target_feerate_per_kw()
if feerate is None:
raise NoDynamicFeeEstimates()
# we set a channel type for internal bookkeeping
open_channel_tlvs = {}
assert self.their_features.supports(LnFeatures.OPTION_STATIC_REMOTEKEY_OPT)
@@ -1598,6 +1600,11 @@ class Peer(Logger, EventListener):
# We should let them know:
self._send_channel_reestablish(chan)
return
if self.network.blockchain().is_tip_stale() \
or not self.lnworker.wallet.is_up_to_date() \
or self.lnworker.current_target_feerate_per_kw() is None:
# don't try to reestablish until we can do fee estimation and are up-to-date
return
# if we get here, we will try to do a proper reestablish
if not (ChannelState.PREOPENING < chan.get_state() < ChannelState.FORCE_CLOSING):
raise Exception(f"unexpected {chan.get_state()=} for reestablish")
@@ -2590,12 +2597,16 @@ class Peer(Logger, EventListener):
return
if chan.get_state() != ChannelState.OPEN:
return
feerate_per_kw = self.lnworker.current_target_feerate_per_kw()
def does_chan_fee_need_update(chan_feerate: Union[float, int]) -> bool:
feerate_per_kw: Optional[int] = self.lnworker.current_target_feerate_per_kw()
if feerate_per_kw is None:
return
def does_chan_fee_need_update(chan_feerate: Union[float, int]) -> Optional[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 = self.lnworker.current_low_feerate_per_kw() # type: Optional[Union[float, int]]
if low_fee is None:
return None
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)

View File

@@ -3000,21 +3000,28 @@ class LNWallet(LNWorker):
else:
await self.taskgroup.spawn(self.reestablish_peer_for_given_channel(chan))
def current_target_feerate_per_kw(self) -> int:
def current_target_feerate_per_kw(self) -> Optional[int]:
if self.network.fee_estimates.has_data():
feerate_per_kvbyte = self.network.fee_estimates.eta_target_to_fee(FEE_LN_ETA_TARGET)
else:
if constants.net is not constants.BitcoinRegtest:
return None
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:
def current_low_feerate_per_kw(self) -> Optional[int]:
if constants.net is constants.BitcoinRegtest:
feerate_per_kvbyte = 0
else:
if not self.network.fee_estimates.has_data():
return None
feerate_per_kvbyte = self.network.fee_estimates.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())
current_target_feerate = self.current_target_feerate_per_kw()
if not current_target_feerate:
return None
low_feerate_per_kw = min(low_feerate_per_kw, current_target_feerate)
return low_feerate_per_kw
def create_channel_backup(self, channel_id: bytes):

View File

@@ -44,8 +44,7 @@ from electrum.lnutil import LOCAL, REMOTE
from electrum.invoices import PR_PAID, PR_UNPAID, Invoice
from electrum.interface import GracefulDisconnect
from electrum.simple_config import SimpleConfig
from electrum.fee_policy import FeeTimeEstimates
from electrum.fee_policy import FeeTimeEstimates, FEE_ETA_TARGETS
from .test_lnchannel import create_test_channels
from .test_bitcoin import needs_test_with_all_chacha20_implementations
@@ -69,6 +68,7 @@ class MockNetwork:
self.lnwatcher = None
self.interface = None
self.fee_estimates = FeeTimeEstimates()
self.populate_fee_estimates()
self.config = config
self.asyncio_loop = util.get_asyncio_loop()
self.channel_db = ChannelDB(self)
@@ -95,6 +95,10 @@ class MockNetwork:
async def try_broadcasting(self, tx, name):
await self.broadcast_transaction(tx)
def populate_fee_estimates(self):
for target in FEE_ETA_TARGETS[:-1]:
self.fee_estimates.set_data(target, 50000 // target)
class MockBlockchain:
@@ -144,6 +148,9 @@ class MockWallet:
# note: sweep is not tested here, only in regtest
return "tb1qqu5newtapamjchgxf0nty6geuykhvwas45q4q4"
def is_up_to_date(self):
return True
class MockLNGossip:
def get_sync_progress_estimate(self):