feat: add configurable welist server URL (advanced mode only)
- Add NoServersForChainError(chain, url, reason) with error details - Expose WELIST_SERVER setting in plugin dialog, hidden in basic mode - Basic mode uses factory default; advanced mode uses configured URL only - Show descriptive error with URL and reason when advanced-mode fetch fails
This commit is contained in:
@@ -77,6 +77,14 @@ class Willexecutors:
|
|||||||
"""Raised when the welist server responds but returns no data for the
|
"""Raised when the welist server responds but returns no data for the
|
||||||
requested chain, indicating no active servers for that network."""
|
requested chain, indicating no active servers for that network."""
|
||||||
|
|
||||||
|
def __init__(self, chain, url=None, reason=None):
|
||||||
|
self.chain = chain
|
||||||
|
self.url = url
|
||||||
|
self.reason = reason
|
||||||
|
super().__init__(
|
||||||
|
f"NoServersForChainError: chain={chain} url={url} reason={reason}"
|
||||||
|
)
|
||||||
|
|
||||||
# Expose the networking constants as class attributes so the GUI layer can
|
# Expose the networking constants as class attributes so the GUI layer can
|
||||||
# reference them (e.g. to show the "Xs / DEADLINEs" countdown) without
|
# reference them (e.g. to show the "Xs / DEADLINEs" countdown) without
|
||||||
# importing module-level names. Single source of truth: the module
|
# importing module-level names. Single source of truth: the module
|
||||||
|
|||||||
@@ -462,6 +462,10 @@ class Plugin(BalPlugin):
|
|||||||
self.update_all()
|
self.update_all()
|
||||||
return
|
return
|
||||||
self.USER_TYPE.set("advanced" if idx == 1 else "basic")
|
self.USER_TYPE.set("advanced" if idx == 1 else "basic")
|
||||||
|
# Show/hide the welist server row (advanced-only).
|
||||||
|
basic = idx != 1
|
||||||
|
for w in (lbl_welist_server, edit_welist_server, help_welist_server):
|
||||||
|
w.setVisible(not basic)
|
||||||
self.update_all()
|
self.update_all()
|
||||||
|
|
||||||
user_type_combo.currentIndexChanged.connect(on_user_type_change)
|
user_type_combo.currentIndexChanged.connect(on_user_type_change)
|
||||||
@@ -474,6 +478,7 @@ class Plugin(BalPlugin):
|
|||||||
# with an external app, so the setting is no longer needed in the dialog.
|
# with an external app, so the setting is no longer needed in the dialog.
|
||||||
edit_event_summary = BalLineEdit(self.EVENT_SUMMARY)
|
edit_event_summary = BalLineEdit(self.EVENT_SUMMARY)
|
||||||
edit_event_description = BalTextEdit(self.EVENT_DESCRIPTION)
|
edit_event_description = BalTextEdit(self.EVENT_DESCRIPTION)
|
||||||
|
edit_welist_server = BalLineEdit(self.WELIST_SERVER)
|
||||||
|
|
||||||
heir_repush = QPushButton("Rebroadcast transactions")
|
heir_repush = QPushButton("Rebroadcast transactions")
|
||||||
heir_repush.clicked.connect(partial(self.broadcast_transactions, True))
|
heir_repush.clicked.connect(partial(self.broadcast_transactions, True))
|
||||||
@@ -637,12 +642,27 @@ class Plugin(BalPlugin):
|
|||||||
"field and the Raw/Date selector."
|
"field and the Raw/Date selector."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
grid.addWidget(heir_repush, 9, 0)
|
# Welist server URL: shown only in ADVANCED mode. In BASIC mode the
|
||||||
|
# factory default is always used and the setting is hidden.
|
||||||
|
lbl_welist_server = QLabel(_("Welist Server URL"))
|
||||||
|
help_welist_server = HelpButton(
|
||||||
|
"URL of the server that provides the will-executor list. "
|
||||||
|
"Only used in ADVANCED mode."
|
||||||
|
)
|
||||||
|
grid.addWidget(lbl_welist_server, 9, 0)
|
||||||
|
grid.addWidget(edit_welist_server, 9, 1)
|
||||||
|
grid.addWidget(help_welist_server, 9, 2)
|
||||||
|
# Initial visibility: hidden in basic, visible in advanced.
|
||||||
|
basic_init = str(self.USER_TYPE.get()).lower() != "advanced"
|
||||||
|
for w in (lbl_welist_server, edit_welist_server, help_welist_server):
|
||||||
|
w.setVisible(not basic_init)
|
||||||
|
|
||||||
|
grid.addWidget(heir_repush, 10, 0)
|
||||||
grid.addWidget(
|
grid.addWidget(
|
||||||
HelpButton(
|
HelpButton(
|
||||||
"Broadcast all transactions to willexecutors including those already pushed"
|
"Broadcast all transactions to willexecutors including those already pushed"
|
||||||
),
|
),
|
||||||
9,
|
10,
|
||||||
2,
|
2,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -673,6 +693,7 @@ class Plugin(BalPlugin):
|
|||||||
(self.NO_WILLEXECUTOR, heir_no_willexecutor, "check"),
|
(self.NO_WILLEXECUTOR, heir_no_willexecutor, "check"),
|
||||||
(self.EVENT_SUMMARY, edit_event_summary, "line"),
|
(self.EVENT_SUMMARY, edit_event_summary, "line"),
|
||||||
(self.EVENT_DESCRIPTION, edit_event_description, "text"),
|
(self.EVENT_DESCRIPTION, edit_event_description, "text"),
|
||||||
|
(self.WELIST_SERVER, edit_welist_server, "line"),
|
||||||
]
|
]
|
||||||
for cfg, widget, kind in resets:
|
for cfg, widget, kind in resets:
|
||||||
# Persist the default value back into the Electrum config.
|
# Persist the default value back into the Electrum config.
|
||||||
|
|||||||
@@ -1093,26 +1093,26 @@ class BalWindow:
|
|||||||
def fetch_will_executors_list(self, old_willexecutors):
|
def fetch_will_executors_list(self, old_willexecutors):
|
||||||
"""Download the will-executor list (runs inside the TaskThread worker).
|
"""Download the will-executor list (runs inside the TaskThread worker).
|
||||||
|
|
||||||
Tries the configured server first, then the original hardcoded endpoint,
|
In BASIC mode the factory-default welist server is used and the setting
|
||||||
so a stale/bad config value cannot break the download. Detailed
|
is hidden. In ADVANCED mode the user-configured URL is used exclusively
|
||||||
per-attempt diagnostics are written to the Electrum log only; the user
|
(no fallback), and any failure produces a descriptive error.
|
||||||
sees a simple message. No business logic in ``bal.core`` is changed.
|
|
||||||
|
|
||||||
Returns the downloaded dict (empty ``{}`` on failure).
|
Returns the downloaded dict (empty ``{}`` on failure).
|
||||||
"""
|
"""
|
||||||
chainname = BalPlugin.chainname
|
chainname = BalPlugin.chainname
|
||||||
configured = self.bal_plugin.WELIST_SERVER.get()
|
basic = self.bal_plugin.is_basic_mode()
|
||||||
candidates = []
|
|
||||||
for base in (configured, "https://welist.bitcoin-after.life/"):
|
if basic:
|
||||||
if not base:
|
base = self.bal_plugin.WELIST_SERVER.default
|
||||||
continue
|
else:
|
||||||
base = base if base.endswith("/") else base + "/"
|
base = self.bal_plugin.WELIST_SERVER.get()
|
||||||
url = f"{base}data/{chainname}?page=0&limit=100"
|
base = base if base.endswith("/") else base + "/"
|
||||||
if url not in candidates:
|
url = f"{base}data/{chainname}?page=0&limit=100"
|
||||||
candidates.append(url)
|
candidates = [url]
|
||||||
|
|
||||||
result = {}
|
result = {}
|
||||||
any_server_reached = False
|
any_server_reached = False
|
||||||
|
last_error = None
|
||||||
net = Network.get_instance()
|
net = Network.get_instance()
|
||||||
_logger.info(f"fetch_will_executors_list: network present = {net is not None}")
|
_logger.info(f"fetch_will_executors_list: network present = {net is not None}")
|
||||||
for url in candidates:
|
for url in candidates:
|
||||||
@@ -1141,11 +1141,20 @@ class BalWindow:
|
|||||||
break
|
break
|
||||||
_logger.warning(f"fetch_will_executors_list: {url} -> empty response")
|
_logger.warning(f"fetch_will_executors_list: {url} -> empty response")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
last_error = str(e)
|
||||||
_logger.error(
|
_logger.error(
|
||||||
f"fetch_will_executors_list: {url} -> {type(e).__name__}: {e}"
|
f"fetch_will_executors_list: {url} -> {type(e).__name__}: {e}"
|
||||||
)
|
)
|
||||||
if not result and any_server_reached:
|
if not result:
|
||||||
raise Willexecutors.NoServersForChainError(chainname)
|
if not basic:
|
||||||
|
# Advanced mode: always raise with full details.
|
||||||
|
raise Willexecutors.NoServersForChainError(
|
||||||
|
chainname,
|
||||||
|
url=url,
|
||||||
|
reason=last_error or "empty response",
|
||||||
|
)
|
||||||
|
if any_server_reached:
|
||||||
|
raise Willexecutors.NoServersForChainError(chainname)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# Simple, user-facing message shown when the download fails for any reason
|
# Simple, user-facing message shown when the download fails for any reason
|
||||||
@@ -1210,11 +1219,23 @@ class BalWindow:
|
|||||||
def on_failure(exc_info):
|
def on_failure(exc_info):
|
||||||
_logger.error(f"download_list failed: {exc_info}")
|
_logger.error(f"download_list failed: {exc_info}")
|
||||||
if isinstance(exc_info[1], Willexecutors.NoServersForChainError):
|
if isinstance(exc_info[1], Willexecutors.NoServersForChainError):
|
||||||
chainname = BalPlugin.chainname
|
err = exc_info[1]
|
||||||
self.show_warning(_(
|
if err.url:
|
||||||
f"No active will-executor found for the "
|
# Advanced mode: show the actual URL and error reason.
|
||||||
f"{chainname} network."
|
self.show_warning(_(
|
||||||
))
|
f"Could not reach the configured welist server.\n\n"
|
||||||
|
f"Server: {err.url}\n"
|
||||||
|
f"Error: {err.reason}\n\n"
|
||||||
|
f"Please verify the welist server URL in the plugin "
|
||||||
|
f"settings."
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
# Basic mode: the server responded but has no data for this
|
||||||
|
# chain.
|
||||||
|
self.show_warning(_(
|
||||||
|
f"No active will-executor found for the "
|
||||||
|
f"{err.chain} network."
|
||||||
|
))
|
||||||
else:
|
else:
|
||||||
self.show_warning(_(self.DOWNLOAD_FAILED_MESSAGE))
|
self.show_warning(_(self.DOWNLOAD_FAILED_MESSAGE))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user