Merge pull request #9889 from f321x/fix_suggest_peer_block
fix: prevent lnrater from blocking if no good peers
This commit is contained in:
@@ -93,7 +93,8 @@ class NewChannelDialog(WindowModalDialog):
|
|||||||
if not nodeid:
|
if not nodeid:
|
||||||
self.remote_nodeid.setText("")
|
self.remote_nodeid.setText("")
|
||||||
self.remote_nodeid.setPlaceholderText(
|
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:
|
else:
|
||||||
self.remote_nodeid.setText(nodeid)
|
self.remote_nodeid.setText(nodeid)
|
||||||
self.remote_nodeid.repaint() # macOS hack for #6269
|
self.remote_nodeid.repaint() # macOS hack for #6269
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ from .logging import Logger
|
|||||||
from .util import profiler, get_running_loop
|
from .util import profiler, get_running_loop
|
||||||
from .lnrouter import fee_for_edge_msat
|
from .lnrouter import fee_for_edge_msat
|
||||||
from .lnutil import LnFeatures, ln_compare_features, IncompatibleLightningFeatures
|
from .lnutil import LnFeatures, ln_compare_features, IncompatibleLightningFeatures
|
||||||
|
from .network import Network
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .network import Network
|
|
||||||
from .channel_db import Policy, NodeInfo
|
from .channel_db import Policy, NodeInfo
|
||||||
from .lnchannel import ShortChannelID
|
from .lnchannel import ShortChannelID
|
||||||
from .lnworker import LNWallet
|
from .lnworker import LNWallet
|
||||||
@@ -86,16 +86,12 @@ class LNRater(Logger):
|
|||||||
self._last_progress_percent = 0
|
self._last_progress_percent = 0
|
||||||
|
|
||||||
def maybe_analyze_graph(self):
|
def maybe_analyze_graph(self):
|
||||||
loop = self.network.asyncio_loop
|
Network.run_from_another_thread(self._maybe_analyze_graph())
|
||||||
fut = asyncio.run_coroutine_threadsafe(self._maybe_analyze_graph(), loop)
|
|
||||||
fut.result()
|
|
||||||
|
|
||||||
def analyze_graph(self):
|
def analyze_graph(self):
|
||||||
"""Forces a graph analysis, e.g., due to external triggers like
|
"""Forces a graph analysis, e.g., due to external triggers like
|
||||||
the graph info reaching 50%."""
|
the graph info reaching 50%."""
|
||||||
loop = self.network.asyncio_loop
|
Network.run_from_another_thread(self._analyze_graph())
|
||||||
fut = asyncio.run_coroutine_threadsafe(self._analyze_graph(), loop)
|
|
||||||
fut.result()
|
|
||||||
|
|
||||||
async def _maybe_analyze_graph(self):
|
async def _maybe_analyze_graph(self):
|
||||||
"""Analyzes the graph when in early sync stage (>30%) or when caching
|
"""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))
|
self.logger.debug(pformat(channel_policies))
|
||||||
continue
|
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")
|
f"for {len(self._node_stats)} nodes")
|
||||||
|
|
||||||
def _rate_nodes(self):
|
def _rate_nodes(self):
|
||||||
@@ -234,15 +230,18 @@ class LNRater(Logger):
|
|||||||
|
|
||||||
self._node_ratings[n] = weighted_sum(heuristics, heuristics_weights)
|
self._node_ratings[n] = weighted_sum(heuristics, heuristics_weights)
|
||||||
|
|
||||||
def suggest_node_channel_open(self) -> Tuple[bytes, NodeStats]:
|
@profiler
|
||||||
node_keys = list(self._node_stats.keys())
|
def suggest_node_channel_open(self) -> Optional[bytes]:
|
||||||
node_ratings = list(self._node_ratings.values())
|
node_stats = self._node_stats.copy()
|
||||||
|
node_ratings = self._node_ratings.copy()
|
||||||
channel_peers = self.lnworker.channel_peers()
|
channel_peers = self.lnworker.channel_peers()
|
||||||
node_info: Optional["NodeInfo"] = None
|
node_info: Optional["NodeInfo"] = None
|
||||||
|
|
||||||
while True:
|
while node_stats:
|
||||||
# randomly pick nodes weighted by node_rating
|
# 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 should have compatible features
|
||||||
node_info = self.network.channel_db.get_node_infos().get(pk, None)
|
node_info = self.network.channel_db.get_node_infos().get(pk, None)
|
||||||
peer_features = LnFeatures(node_info.features)
|
peer_features = LnFeatures(node_info.features)
|
||||||
@@ -265,13 +264,16 @@ class LNRater(Logger):
|
|||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
break
|
break
|
||||||
|
else:
|
||||||
|
self.logger.info(f"no suitable channel peer found")
|
||||||
|
return None
|
||||||
|
|
||||||
alias = node_info.alias if node_info else 'unknown node alias'
|
alias = node_info.alias if node_info else 'unknown node alias'
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"node rating for {alias}:\n"
|
f"node rating for {alias}:\n"
|
||||||
f"{pformat(self._node_stats[pk])} (score {self._node_ratings[pk]})")
|
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]:
|
def suggest_peer(self) -> Optional[bytes]:
|
||||||
"""Suggests a LN node to open a channel with.
|
"""Suggests a LN node to open a channel with.
|
||||||
@@ -279,6 +281,6 @@ class LNRater(Logger):
|
|||||||
"""
|
"""
|
||||||
self.maybe_analyze_graph()
|
self.maybe_analyze_graph()
|
||||||
if self._node_ratings:
|
if self._node_ratings:
|
||||||
return self.suggest_node_channel_open()[0]
|
return self.suggest_node_channel_open()
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|||||||
Reference in New Issue
Block a user