network: dedupe qt/qml get_disconnected_server_addrs code
What changed, and why it matters
This commit is a simple internal code cleanup. It moves duplicated logic for listing disconnected servers from two user-interface files into a single shared helper method in the core networking module. There is no change in behavior visible to users and no security fix or vulnerability introduced.
No security action needed. Treat as ordinary refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors the construction of the disconnected-server address list by introducing Network.get_disconnected_server_addrs() in electrum/network.py. The Qt (network_dialog.py) and QML (qeserverlistmodel.py) GUI code now call this shared helper instead of each containing its own copy of the filtering/sorting logic. Type hints were added to get_servers() and get_blockchains(). The logic, including the FIXME about filtering out bookmarked servers from non-preferred protocols, is preserved verbatim.
Changed components
electrum/network.pyelectrum/gui/qt/network_dialog.pyelectrum/gui/qml/qeserverlistmodel.pyInspect captured patch +35 / −40
diff --git a/electrum/gui/qml/qeserverlistmodel.py b/electrum/gui/qml/qeserverlistmodel.py
index e781758..b2e2222 100644
--- a/electrum/gui/qml/qeserverlistmodel.py
+++ b/electrum/gui/qml/qeserverlistmodel.py
@@ -111,28 +111,18 @@ class QEServerListModel(QAbstractListModel, QtEventListener):
servers.append(server)
# disconnected servers
- all_servers = self.network.get_servers()
- connected_hosts = set([iface.host for ifaces in chains.values() for iface in ifaces])
- protocol = PREFERRED_NETWORK_PROTOCOL
- for _host, d in sorted(all_servers.items()):
- if _host in connected_hosts:
- continue
- if _host.endswith('.onion') and not self.network.is_proxy_tor:
- continue
- port = d.get(protocol)
- if port:
- s = ServerAddr(_host, port, protocol=protocol)
- server = {
- 'chain': '',
- 'chain_height': 0,
- 'height': 0,
- 'is_primary': False,
- 'is_connected': False,
- 'name': s.net_addr_str()
- }
- server['address'] = server['name']
-
- servers.append(server)
+ for s in self.network.get_disconnected_server_addrs():
+ server = {
+ 'chain': '',
+ 'chain_height': 0,
+ 'height': 0,
+ 'is_primary': False,
+ 'is_connected': False,
+ 'name': s.net_addr_str()
+ }
+ server['address'] = server['name']
+
+ servers.append(server)
self.beginInsertRows(QModelIndex(), 0, len(servers) - 1)
self._servers = servers
diff --git a/electrum/gui/qt/network_dialog.py b/electrum/gui/qt/network_dialog.py
index 4d7c680..2b97064 100644
--- a/electrum/gui/qt/network_dialog.py
+++ b/electrum/gui/qt/network_dialog.py
@@ -149,9 +149,6 @@ class NodesListWidget(QTreeWidget):
def update(self):
self.clear()
network = self.network
- servers = self.network.get_servers()
-
- use_tor = bool(network.is_proxy_tor)
# connected servers
connected_servers_item = QTreeWidgetItem([_("Connected nodes"), ''])
@@ -185,18 +182,7 @@ class NodesListWidget(QTreeWidget):
# disconnected servers
disconnected_servers_item = QTreeWidgetItem([_("Other known servers"), ""])
disconnected_servers_item.setData(0, self.ITEMTYPE_ROLE, self.ItemType.TOPLEVEL)
- connected_hosts = set([iface.host for ifaces in chains.values() for iface in ifaces])
- protocol = PREFERRED_NETWORK_PROTOCOL
- server_addrs = [
- ServerAddr(_host, port, protocol=protocol)
- for _host, d in servers.items()
- if (port := d.get(protocol))]
- server_addrs.sort(key=lambda x: (-network.is_server_bookmarked(x), str(x)))
- for server in server_addrs:
- if server.host in connected_hosts:
- continue
- if server.host.endswith('.onion') and not use_tor:
- continue
+ for server in network.get_disconnected_server_addrs():
item = QTreeWidgetItem([server.net_addr_str(), ""])
item.setData(0, self.ITEMTYPE_ROLE, self.ItemType.DISCONNECTED_SERVER)
item.setData(0, self.SERVER_ADDR_ROLE, server)
diff --git a/electrum/network.py b/electrum/network.py
index 966a31e..2bff22e 100644
--- a/electrum/network.py
+++ b/electrum/network.py
@@ -30,7 +30,7 @@ import threading
import json
from typing import (
NamedTuple, Optional, Sequence, List, Dict, Tuple, TYPE_CHECKING, Iterable, Set, Any, TypeVar,
- Callable
+ Callable, Mapping,
)
import copy
import functools
@@ -624,7 +624,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
@with_recent_servers_lock
- def get_servers(self):
+ def get_servers(self) -> Mapping[str, Mapping[str, str]]:
# note: order of sources when adding servers here is crucial!
# don't let "server_peers" overwrite anything,
# otherwise main server can eclipse the client
@@ -659,6 +659,25 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
out = filter_noonion(out)
return out
+ def get_disconnected_server_addrs(self) -> Sequence[ServerAddr]:
+ servers = self.get_servers()
+ disconnected_server_addrs = [] # type: List[ServerAddr]
+ chains = self.get_blockchains()
+ connected_hosts = set([iface.host for ifaces in chains.values() for iface in ifaces])
+ protocol = PREFERRED_NETWORK_PROTOCOL
+ server_addrs = [
+ ServerAddr(_host, port, protocol=protocol)
+ for _host, d in servers.items()
+ if (port := d.get(protocol))] # FIXME this filters out even bookmarked servers from other protocols
+ server_addrs.sort(key=lambda x: (-self.is_server_bookmarked(x), str(x)))
+ for server in server_addrs:
+ if server.host in connected_hosts:
+ continue
+ if server.host.endswith('.onion') and not self.is_proxy_tor:
+ continue
+ disconnected_server_addrs.append(server)
+ return disconnected_server_addrs
+
def _get_next_server_to_try(self) -> Optional[ServerAddr]:
now = time.time()
with self.interfaces_lock:
@@ -1122,7 +1141,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
self._blockchain = interface.blockchain
return self._blockchain
- def get_blockchains(self):
+ def get_blockchains(self) -> Mapping[str, Sequence[Interface]]:
out = {} # blockchain_id -> list(interfaces)
with blockchain.blockchains_lock: blockchain_items = list(blockchain.blockchains.items())
with self.interfaces_lock: interfaces_values = list(self.interfaces.values())
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.