1
0

network dialog: replace checkboxes with tri-state connect_mode ComboBox

If oneserver is enabled, display 'Fork detection disabled' in the split_label
This commit is contained in:
ThomasV
2025-05-28 12:28:38 +02:00
parent d0c429e87f
commit b78935521b
2 changed files with 98 additions and 53 deletions

View File

@@ -104,3 +104,34 @@ MSG_TERMS_OF_USE = (
)
TERMS_OF_USE_LATEST_VERSION : int = 1 # bump this if we want users re-prompted due to changes
MSG_CONNECTMODE_AUTOCONNECT = _('Auto-connect')
MSG_CONNECTMODE_MANUAL = _('Manual server selection')
MSG_CONNECTMODE_ONESERVER = _('Connect only to a single server')
MSG_CONNECTMODE_SERVER_HELP = _(
"Electrum connects to a unique server in order to receive your transaction history. "
"This server will learn your wallet adddresses."
)
MSG_CONNECTMODE_NODES_HELP = _(
"In addition to your history server, Electrum will try to maintain connections with ~10 extra servers, in order to download block headers and find out the longest blockchain. "
"These servers are only used for block header notifications and fee estimates; they do not learn your wallet addresses. "
"Getting block headers from multiple sources is useful to detect lagging servers and forks. "
"Fork detection is security-critical for determining number of confirmations."
)
MSG_CONNECTMODE_AUTOCONNECT_HELP = _(
"Electrum will always use a history server that is on the longest blockchain. "
"If your current server is unresponsive or lagging, Electrum will switch to another server."
)
MSG_CONNECTMODE_MANUAL_HELP = _(
"Electrum will stay with the server you selected. It will warn you if your server is lagging."
)
MSG_CONNECTMODE_ONESERVER_HELP = _(
"Electrum will stay with the server you selected, and it will not connect to additional nodes. "
"This will disable fork detection. "
"This mode is only intended for connecting to your own fully trusted server. "
"Using this option on a public server is a security risk and is discouraged."
)

View File

@@ -39,6 +39,7 @@ from electrum.interface import ServerAddr, PREFERRED_NETWORK_PROTOCOL
from electrum.network import Network, ProxySettings, is_valid_host, is_valid_port
from electrum.logging import get_logger
from electrum.util import is_valid_websocket_url
from electrum.gui import messages
from .util import (
Buttons, CloseButton, HelpButton, read_QIcon, char_width_in_lineedit, PasswordLineEdit, QtEventListener,
@@ -61,7 +62,7 @@ class NetworkDialog(QDialog, QtEventListener):
self._blockchain_tab = ServerWidget(network)
self._proxy_tab = ProxyWidget(network)
self._nostr_tab = NostrWidget(network)
tabs.addTab(self._blockchain_tab, _('Electrum servers'))
tabs.addTab(self._blockchain_tab, _('Server'))
tabs.addTab(self._nostr_tab, _('Nostr'))
tabs.addTab(self._proxy_tab, _('Proxy'))
vbox = QVBoxLayout(self)
@@ -362,7 +363,17 @@ class ProxyWidget(QWidget):
self.update()
class ConnectMode(IntEnum):
AUTOCONNECT = 0
MANUAL = 1
ONESERVER = 2
class ServerWidget(QWidget, QtEventListener):
CONNECT_MODES = {
ConnectMode.AUTOCONNECT: messages.MSG_CONNECTMODE_AUTOCONNECT,
ConnectMode.MANUAL: messages.MSG_CONNECTMODE_MANUAL,
ConnectMode.ONESERVER: messages.MSG_CONNECTMODE_ONESERVER,
}
def __init__(self, network: Network, parent=None):
super().__init__(parent)
@@ -373,46 +384,47 @@ class ServerWidget(QWidget, QtEventListener):
grid = QGridLayout()
msg = ' '.join([
_("Electrum connects to several nodes in order to download block headers and find out the longest blockchain."),
_("This blockchain is used to verify the transactions sent by your transaction server.")
])
self.connect_combo = QComboBox()
for i, v in sorted(self.CONNECT_MODES.items()):
self.connect_combo.addItem(v, i)
self.connect_combo.currentIndexChanged.connect(self.on_server_settings_changed)
grid.addWidget(QLabel(_('Connection mode') + ':'), 0, 0)
msg = (
f"""
{messages.MSG_CONNECTMODE_SERVER_HELP}<br/><br/>
{messages.MSG_CONNECTMODE_NODES_HELP}
<ul>
<li><b>{messages.MSG_CONNECTMODE_AUTOCONNECT}</b>: {messages.MSG_CONNECTMODE_AUTOCONNECT_HELP}</li>
<li><b>{messages.MSG_CONNECTMODE_MANUAL}</b>: {messages.MSG_CONNECTMODE_MANUAL_HELP}</li>
<li><b>{messages.MSG_CONNECTMODE_ONESERVER}</b>: {messages.MSG_CONNECTMODE_ONESERVER_HELP}</li>
</ul>
"""
)
grid.addWidget(HelpButton(msg), 0, 4)
grid.addWidget(self.connect_combo, 0, 1, 1, 3)
self.status_label_header = QLabel(_('Status') + ':')
self.status_label = QLabel('')
self.status_label_helpbutton = HelpButton(msg)
grid.addWidget(self.status_label_header, 0, 0)
grid.addWidget(self.status_label, 0, 1, 1, 3)
grid.addWidget(self.status_label_helpbutton, 0, 4)
self.autoconnect_cb = QCheckBox(self.config.cv.NETWORK_AUTO_CONNECT.get_short_desc())
self.autoconnect_cb.stateChanged.connect(self.on_server_settings_changed)
grid.addWidget(self.autoconnect_cb, 1, 0, 1, 3)
grid.addWidget(HelpButton(self.config.cv.NETWORK_AUTO_CONNECT.get_long_desc()), 1, 4)
self.one_server_cb = QCheckBox(self.config.cv.NETWORK_ONESERVER.get_short_desc())
self.one_server_cb.setEnabled(self.config.cv.NETWORK_ONESERVER.is_modifiable())
self.one_server_cb.stateChanged.connect(self.on_server_settings_changed)
grid.addWidget(self.one_server_cb, 2, 0, 1, 3)
grid.addWidget(HelpButton(self.config.cv.NETWORK_ONESERVER.get_long_desc()), 2, 4)
self.status_label_helpbutton = HelpButton(messages.MSG_CONNECTMODE_NODES_HELP)
grid.addWidget(self.status_label_header, 1, 0)
grid.addWidget(self.status_label, 1, 1, 1, 3)
grid.addWidget(self.status_label_helpbutton, 1, 4)
self.server_e = QLineEdit()
self.server_e.editingFinished.connect(self.on_server_settings_changed)
msg = _("Electrum sends your wallet addresses to a single server, in order to receive your transaction history.")
grid.addWidget(QLabel(_('Server') + ':'), 3, 0)
grid.addWidget(self.server_e, 3, 1, 1, 3)
grid.addWidget(HelpButton(msg), 3, 4)
grid.addWidget(QLabel(_('Server') + ':'), 4, 0)
grid.addWidget(self.server_e, 4, 1, 1, 3)
grid.addWidget(HelpButton(messages.MSG_CONNECTMODE_SERVER_HELP), 4, 4)
msg = _('This is the height of your local copy of the blockchain.')
self.height_label_header = QLabel(_('Blockchain') + ':')
self.height_label = QLabel('')
self.height_label_helpbutton = HelpButton(msg)
grid.addWidget(self.height_label_header, 4, 0)
grid.addWidget(self.height_label, 4, 1)
grid.addWidget(self.height_label_helpbutton, 4, 4)
grid.addWidget(self.height_label_header, 2, 0)
grid.addWidget(self.height_label, 2, 1)
grid.addWidget(self.height_label_helpbutton, 2, 4)
self.split_label = QLabel('')
grid.addWidget(self.split_label, 5, 0, 1, 3)
grid.addWidget(self.split_label, 3, 1, 1, 3)
self.layout().addLayout(grid)
@@ -439,32 +451,28 @@ class ServerWidget(QWidget, QtEventListener):
self.nodes_list_widget.update() # NOTE: move event handling to widget itself?
self.update()
def is_auto_connect(self):
return self.connect_combo.currentIndex() == ConnectMode.AUTOCONNECT
def is_one_server(self):
return self.connect_combo.currentIndex() == ConnectMode.ONESERVER
def on_server_settings_changed(self):
if not self.network._was_started:
self.update()
return
auto_connect = self.autoconnect_cb.isChecked()
one_server = self.one_server_cb.isChecked()
self.autoconnect_cb.setEnabled(not one_server and self.config.cv.NETWORK_AUTO_CONNECT.is_modifiable())
self.one_server_cb.setEnabled(not auto_connect and self.config.cv.NETWORK_ONESERVER.is_modifiable())
server = self.server_e.text().strip()
net_params = self.network.get_parameters()
if server != net_params.server or auto_connect != net_params.auto_connect or one_server != net_params.oneserver:
if server != net_params.server or self.is_auto_connect() != net_params.auto_connect or self.is_one_server() != net_params.oneserver:
self.set_server()
def update(self):
auto_connect = self.autoconnect_cb.isChecked()
one_server = self.one_server_cb.isChecked()
self.autoconnect_cb.setEnabled(not one_server and self.config.cv.NETWORK_AUTO_CONNECT.is_modifiable())
self.one_server_cb.setEnabled(not auto_connect and self.config.cv.NETWORK_ONESERVER.is_modifiable())
self.server_e.setEnabled(self.config.cv.NETWORK_SERVER.is_modifiable() and not auto_connect)
self.server_e.setEnabled(self.config.cv.NETWORK_SERVER.is_modifiable() and not self.is_auto_connect())
for item in [
self.status_label_header, self.status_label, self.status_label_helpbutton,
self.height_label_header, self.height_label, self.height_label_helpbutton]:
item.setVisible(self.network._was_started)
msg = ''
msg = _('Fork detection disabled') if self.is_one_server() else ''
if self.network._was_started:
# Network was started, so we don't run in initial setup wizard.
# behavior in this case is to apply changes immediately.
@@ -477,8 +485,8 @@ class ServerWidget(QWidget, QtEventListener):
chain = self.network.blockchain()
forkpoint = chain.get_max_forkpoint()
name = chain.get_name()
msg = _('Chain split detected at block {0}').format(forkpoint) + '\n'
if auto_connect:
msg = _('Fork detected at block {0}').format(forkpoint) + '\n'
if self.is_auto_connect():
msg += _('You are following branch {}').format(name)
else:
msg += _('Your server is on branch {0} ({1} blocks)').format(name, chain.get_branch_size())
@@ -486,14 +494,13 @@ class ServerWidget(QWidget, QtEventListener):
def update_from_config(self):
auto_connect = self.config.NETWORK_AUTO_CONNECT
self.autoconnect_cb.setChecked(auto_connect)
one_server = self.config.NETWORK_ONESERVER
self.one_server_cb.setChecked(one_server)
v = ConnectMode.AUTOCONNECT if auto_connect else ConnectMode.ONESERVER if one_server else ConnectMode.MANUAL
self.connect_combo.setCurrentIndex(v)
server = self.config.NETWORK_SERVER
self.server_e.setText(server)
self.autoconnect_cb.setEnabled(self.config.cv.NETWORK_AUTO_CONNECT.is_modifiable() and not one_server)
self.one_server_cb.setEnabled(self.config.cv.NETWORK_ONESERVER.is_modifiable() and not auto_connect)
self.server_e.setEnabled(self.config.cv.NETWORK_SERVER.is_modifiable() and not auto_connect)
self.nodes_list_widget.setEnabled(self.config.cv.NETWORK_SERVER.is_modifiable())
@@ -514,9 +521,11 @@ class ServerWidget(QWidget, QtEventListener):
raise Exception("failed to parse server")
except Exception:
return
net_params = net_params._replace(server=server,
auto_connect=self.autoconnect_cb.isChecked(),
oneserver=self.one_server_cb.isChecked())
net_params = net_params._replace(
server=server,
auto_connect=self.is_auto_connect(),
oneserver=self.is_one_server(),
)
self.network.run_from_another_thread(self.network.set_parameters(net_params))
@@ -528,11 +537,16 @@ class NostrWidget(QWidget, QtEventListener):
self.config = network.config
vbox = QVBoxLayout()
self.setLayout(vbox)
nostr_relays_label = HelpLabel.from_configvar(self.config.cv.NOSTR_RELAYS)
grid = QGridLayout()
nostr_relays_label = QLabel(self.config.cv.NOSTR_RELAYS.get_short_desc())
nostr_helpbutton = HelpButton(self.config.cv.NOSTR_RELAYS.get_long_desc())
grid.addWidget(nostr_relays_label, 0, 0)
grid.addWidget(nostr_helpbutton, 0, 1)
vbox.addLayout(grid)
self.relays_list = QListWidget()
self.relay_edit = QLineEdit()
self.relay_edit.textChanged.connect(self.on_relay_edited)
vbox.addWidget(nostr_relays_label)
vbox.addWidget(self.relays_list)
vbox.addStretch()
self.add_button = QPushButton(_('Add'))