1
0

Merge pull request #9889 from f321x/fix_suggest_peer_block

fix: prevent lnrater from blocking if no good peers
This commit is contained in:
ThomasV
2025-06-03 10:54:13 +02:00
committed by GitHub
2 changed files with 19 additions and 16 deletions

View File

@@ -93,7 +93,8 @@ class NewChannelDialog(WindowModalDialog):
if not nodeid:
self.remote_nodeid.setText("")
self.remote_nodeid.setPlaceholderText(
"Please wait until the graph is synchronized to 30%, and then try again.")
_("Couldn't find suitable peer yet, try again later.")
)
else:
self.remote_nodeid.setText(nodeid)
self.remote_nodeid.repaint() # macOS hack for #6269

View File

@@ -18,9 +18,9 @@ from .logging import Logger
from .util import profiler, get_running_loop
from .lnrouter import fee_for_edge_msat
from .lnutil import LnFeatures, ln_compare_features, IncompatibleLightningFeatures
from .network import Network
if TYPE_CHECKING:
from .network import Network
from .channel_db import Policy, NodeInfo
from .lnchannel import ShortChannelID
from .lnworker import LNWallet
@@ -86,16 +86,12 @@ class LNRater(Logger):
self._last_progress_percent = 0
def maybe_analyze_graph(self):
loop = self.network.asyncio_loop
fut = asyncio.run_coroutine_threadsafe(self._maybe_analyze_graph(), loop)
fut.result()
Network.run_from_another_thread(self._maybe_analyze_graph())
def analyze_graph(self):
"""Forces a graph analysis, e.g., due to external triggers like
the graph info reaching 50%."""
loop = self.network.asyncio_loop
fut = asyncio.run_coroutine_threadsafe(self._analyze_graph(), loop)
fut.result()
Network.run_from_another_thread(self._analyze_graph())
async def _maybe_analyze_graph(self):
"""Analyzes the graph when in early sync stage (>30%) or when caching
@@ -197,7 +193,7 @@ class LNRater(Logger):
self.logger.debug(pformat(channel_policies))
continue
self.logger.info(f"node statistics done, calculated statistics"
self.logger.info(f"node statistics done, calculated statistics "
f"for {len(self._node_stats)} nodes")
def _rate_nodes(self):
@@ -234,15 +230,18 @@ class LNRater(Logger):
self._node_ratings[n] = weighted_sum(heuristics, heuristics_weights)
def suggest_node_channel_open(self) -> Tuple[bytes, NodeStats]:
node_keys = list(self._node_stats.keys())
node_ratings = list(self._node_ratings.values())
@profiler
def suggest_node_channel_open(self) -> Optional[bytes]:
node_stats = self._node_stats.copy()
node_ratings = self._node_ratings.copy()
channel_peers = self.lnworker.channel_peers()
node_info: Optional["NodeInfo"] = None
while True:
while node_stats:
# randomly pick nodes weighted by node_rating
pk = choices(node_keys, weights=node_ratings, k=1)[0]
pk = choices(list(node_stats.keys()), weights=list(node_ratings.values()), k=1)[0]
# remove the pk so it doesn't get tried again
node_stats.pop(pk); node_ratings.pop(pk)
# node should have compatible features
node_info = self.network.channel_db.get_node_infos().get(pk, None)
peer_features = LnFeatures(node_info.features)
@@ -265,13 +264,16 @@ class LNRater(Logger):
else:
continue
break
else:
self.logger.info(f"no suitable channel peer found")
return None
alias = node_info.alias if node_info else 'unknown node alias'
self.logger.info(
f"node rating for {alias}:\n"
f"{pformat(self._node_stats[pk])} (score {self._node_ratings[pk]})")
return pk, self._node_stats[pk]
return pk
def suggest_peer(self) -> Optional[bytes]:
"""Suggests a LN node to open a channel with.
@@ -279,6 +281,6 @@ class LNRater(Logger):
"""
self.maybe_analyze_graph()
if self._node_ratings:
return self.suggest_node_channel_open()[0]
return self.suggest_node_channel_open()
else:
return None