fix: prevent lnrater from blocking if no good peers
the while loop in `suggest_node_channel_open()` of lnrater would not break if there are no "good" peers available available. As a result the gui blocks and electrum has to be killed. This can happen for example on signet. This removes the tested pk from the list of candidates so each candidate gets tested only once.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user