diff --git a/electrum/gui/qt/new_channel_dialog.py b/electrum/gui/qt/new_channel_dialog.py index db8fd7809..44bce8a54 100644 --- a/electrum/gui/qt/new_channel_dialog.py +++ b/electrum/gui/qt/new_channel_dialog.py @@ -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 diff --git a/electrum/lnrater.py b/electrum/lnrater.py index 0ff6a3fd5..fbce9f398 100644 --- a/electrum/lnrater.py +++ b/electrum/lnrater.py @@ -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