Sync bal-plugin-ai to bal-electrum-plugin (v0.5.18) #1
Reference in New Issue
Block a user
Delete Branch "sync-v0.5.18"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Full sync of bal-plugin-ai v0.5.18 into bal-electrum-plugin.
The previous main branch content has been preserved in the
archivedbranch.Changes included:
Behavior-preserving refactor of the original BAL plugin with clean separation of business logic from the PyQt GUI. Layout: bal/core/ GUI-free logic (util, plugin_base, heirs, will, willexecutors) bal/gui/qt/ PyQt6 presentation (theme, common, widgets, calendar, dialogs, lists, window, plugin) bal/qt.py Qt entry-point shim (works as internal and external zip plugin) bal/manifest.json standard-conforming metadata Tooling: build_zip.py deterministic, zipimport-friendly archive builder tests/smoke_test.py imports + behavior regression test tests/external_zip_test.py reproduces Electrum's external-zip loading Targets Electrum 4.7.2 + PyQt6. Logic kept byte-identical where possible.* fix(gui): correct window z-order and lifecycle bugs (B1-B10) Sintomi risolti: - S1: le finestre del plugin sparivano dietro Electrum - S2: alcuni meccanismi funzionavano solo dopo chiusura+pulizia di Electrum La logica di business resta BYTE-IDENTICA (nessuna modifica a bal/core/*): cambiano solo parent, modalità, ciclo di vita, cleanup e presentazione. Nuovo modulo bal/gui/qt/window_utils.py con helper centralizzati: - top_level_of, bring_to_front, stop_thread, show_modal, show_on_top Fix per bug: - B1: self.parent -> self._bal_parent (dialogs/lists/widgets); parent = top_level_of(parent) - B2/B9: .show() -> show_on_top()/show_modal()/bring_to_front() - B3: init a caldo _setup_window() replica load_wallet (niente 'restart Electrum') - B4: chiave finestra stabile _window_key() = id(window) - B5: on_close riscritto (no except:pass, log per-step, reset stato) - B6: BalBlockingWaitingDialog ripristina processEvents() - B7/B8: closeEvent/hideEvent -> stop_thread() (stop+wait) + super() - B10: uso di window.tools_menu (no ricerca per titolo localizzato '&Tools') Test: smoke + gui_fixes (regressione B1-B10) + external_zip tutti verdi. Doc aggiornata: DIAGNOSI_GUI.md marca B1-B10 come FIXED. * fix(gui): do not kill task thread on dialog close (download list regression) The B7/B8 change added stop_thread() to BalDialog.closeEvent/hideEvent. But Electrum's TaskThread.on_done runs cb_done (often self.accept, which closes the waiting dialog) BEFORE cb_result (on_success, which updates the will-executor list). Stopping/joining the thread inside closeEvent therefore tore the thread down before on_success ran, silently dropping the downloaded will-executor list ('Download List' appeared to do nothing). Restore the original safe behavior: the base BalDialog no longer stops the thread on close/hide (matching the original plugin, which deliberately left this commented out). Long-lived dialogs that own a thread still stop it explicitly in their own handlers. Adds a regression test asserting BalDialog.closeEvent/hideEvent never call stop_thread. * fix(gui): restore modal exec for waiting dialog + surface download failures Two changes to fix 'Download List' doing nothing: 1) BalWaitingDialog.exe() now keeps the original application-modal exec() (only adding raise/activate for visibility). The earlier switch to window-modal could interfere with how the TaskThread result (on_success, which populates the will-executor list) is delivered via a queued signal while the modal loop is spinning. 2) BalWindow.download_list now logs how many entries were received and, when the result is empty (the core download_list swallows errors and returns {}), shows a warning to the user instead of failing silently. This makes any future network/parse failure visible in the Electrum log and to the user. Business logic in bal/core/* is unchanged. * fix(gui): show the real download error reason in the warning popup When 'Download List' fails, the core download_list returns {} and the cause was only visible in the Electrum log (which is hard to capture). The GUI now re-issues the same raw request when the result is empty and shows the actual exception/reason in the warning popup (e.g. SSL error, timeout, empty server response). Pure GUI-side diagnostics; bal/core/* logic unchanged. * fix(gui): download will-executor list synchronously like the original ROOT CAUSE: the original plugin's 'Download List' button called Willexecutors.download_list() DIRECTLY on the GUI thread (qt.py: WillExecutorWidget.download_list). The refactor instead routed the button through BalWaitingDialog + TaskThread. Electrum's Network.send_http_on_proxy behaves differently depending on the calling thread, and on the user's setup the TaskThread path timed out ('No response from the server'), while the original direct call worked fine. FIX: WillExecutorWidget.download_list now performs the same direct, synchronous download as the original, updates and saves the list, and shows a warning only on genuine failure. The wizard download path (which the original also ran via TaskThread) is left unchanged. Compared against upstream original source (kaibot/bal-electrum-plugin): send_request, handle_response, download_list core logic are byte-identical; only the GUI call site is restored to the original behaviour. * diag(gui): detailed download diagnostics + hardcoded-URL fallback The will-executor download still times out on the user's setup even with the direct (original-style) GUI-thread call, and the URL/request are byte-identical to the working original. To pinpoint the real cause, the Download List button now: - logs whether a Network instance is present; - tries the configured WELIST_SERVER URL AND falls back to the original hardcoded https://welist.bitcoin-after.life/ endpoint (so a stale/bad config value can't break it); - shows the EXACT URL(s) tried and the precise exception per attempt in the warning popup instead of a generic timeout message. bal/core networking remains unchanged. * diag(gui): add direct-HTTPS control probe to download diagnostics When the Electrum-network download fails, also run a plain urllib HTTPS GET (bypassing Electrum's Network/proxy layer) and show its result in the popup. This distinguishes a real connectivity/DNS/firewall problem from an Electrum-network-state problem, so we can finally pinpoint why the request times out only in this build. * fix(gui): unify wizard + button download on one synchronous path with diagnostics The 'No response from the server (timeout?)' popup was coming from the WIZARD download path in window.py (BalWaitingDialog + TaskThread), which was never switched to the original direct call - only the list button had been. Both paths now share BalWindow.fetch_will_executors_list(): a direct, synchronous GUI-thread download (like the original), trying the configured server then the hardcoded fallback, with full diagnostics (exact URL/error per attempt + a direct-HTTPS control probe) shown in the failure popup. This both fixes the wizard timeout and guarantees the same diagnostic popup ('Details (via Electrum network)' + 'Direct connection test') regardless of which UI entry point is used. * fix(gui): clean up will-executor download (waiting dialog + simple message) Root cause of the 'download not working' reports was environmental (the user's network/ISP was resetting the connection to the IPv6/IPv4 host; a VPN fixes it), NOT a plugin bug. Final cleanup of the diagnostic code: - download_list (button + wizard) again uses BalWaitingDialog + TaskThread so the GUI is not frozen and shows a 'Downloading will-executors list...' dialog. - Keep the configured + hardcoded-fallback server URLs and detailed per-attempt diagnostics, but write them to the Electrum log only. - On failure the user now sees a simple English message explaining it is most likely a connection/firewall issue (a VPN often helps), instead of a technical timeout/exception dump. - Removed the urllib control probe from the user-facing popup. bal/core networking unchanged. --------- Co-authored-by: GenSpark AI Developer <ai@genspark.dev>Aggiunge al CHANGELOG_REFACTOR.md (in italiano) le sezioni che mancavano per coprire il refactoring dall'inizio alla fine rispetto al codice originale Gitea: - §9 Correzioni GUI B1-B10 (z-order, parent, modalita, ciclo di vita) + nuovo modulo window_utils.py - §10 Fix download lista will-executor: difetti GUI corretti (closeEvent/ hideEvent non fermano piu il TaskThread; exe() torna a exec()), percorsi pulsante/wizard unificati, causa vera ambientale (WinError 10054 risolto via VPN), pulizia finale + messaggio errore in inglese - §11 Confronto strutturale finale originale Gitea -> refactor (file/righe) - §12 Cronologia commit su GitHub DIAGNOSI_GUI.md: aggiornato lo stato (B1-B10 mergiati in main, PR #2dd6f677).* fix(gui): voci di menu BAL duplicate/condensate dopo riavvio o cambio wallet Sintomo (Windows 11): dopo aver riavviato Electrum o cambiato wallet, le schede Will/Heirs sparivano dalla tab bar e dal menu, e compariva una voce di menu condensata/illeggibile (icona + testo sovrapposti) sotto il logo di Electrum, accanto a 'Portafogli'. Causa: init_menubar_tools veniva eseguito DUE volte sulla stessa finestra. Con il plugin gia abilitato, al riavvio Electrum invoca sia l'hook init_menubar sia il percorso di init a caldo (init_qt -> _setup_window), entrambi chiamano init_menubar_tools -> addTab/addAction duplicati. Nell'originale init_qt faceva return (chiedendo il riavvio) e quindi i menu venivano creati una sola volta; rimuovendo quel return (fix B3) e' emersa la doppia inizializzazione. Fix: - BalWindow._menubar_initialized: guardia di idempotenza. - init_menubar_tools: se gia inizializzato, esce subito (niente duplicati). - on_close: resetta il flag dopo aver rimosso tab/azioni, cosi la stessa finestra puo essere riusata per un altro wallet. - tests/gui_fixes_test.py: regressione che verifica la guardia in __init__, init_menubar_tools e on_close. Logica di business invariata (nessuna modifica a bal/core/*). * fix(gui): ripristina create_status_bar come no-op (come originale) L'elemento di menu condensato/illeggibile sotto il logo di Electrum era causato dal StatusBarButton aggiunto da create_status_bar. Nell'originale Gitea questo hook aveva un 'return' subito dopo il log, PRIMA di costruire il bottone: era quindi disabilitato di proposito. Durante la pulizia del 'dead code' nel refactoring quel return era stato rimosso, riattivando la creazione del bottone -> elemento icona+testo renderizzato nel punto sbagliato dopo riavvio/cambio wallet. Fix: create_status_bar torna a essere un no-op (return), fedele all'originale. Le impostazioni restano raggiungibili da Strumenti -> Plugin. Regressione: gui_fixes_test verifica che create_status_bar non chiami addPermanentWidget. * fix(core): OverflowError su Windows (anno 2038) che rompeva tab/menu BAL CAUSA VERA (dal log Electrum dell'utente, Windows 11): OverflowError: Python int too large to convert to C int window.py __init__ -> create_heirs_tab -> WillSettingsWidget -> on_locktime_change -> BalTimestamp.to_date -> datetime.fromtimestamp(NLOCKTIME_MAX) Su Windows time_t e' a 32 bit, quindi datetime.fromtimestamp() solleva OverflowError per qualsiasi timestamp oltre il 2038 (es. NLOCKTIME_MAX = 2**32-1 = 4294967295, usato come locktime di default/sentinella). Su Linux 64-bit la stessa chiamata funziona: per questo il bug si vedeva solo su Windows e i test su Linux non lo intercettavano. L'eccezione interrompeva BalWindow.__init__ durante init_menubar/load_wallet, lasciando le schede Will/Heirs e la voce di menu a meta' costruzione -> l'elemento grafico condensato/illeggibile sotto il logo di Electrum. FIX (comportamento invariato per tutti i valori normali): - BalTimestamp._safe_fromtimestamp(): datetime.fromtimestamp con clamp a INT32_MAX in caso di OverflowError/OSError/ValueError, esattamente come la funzione get_max_allowed_timestamp() dell'originale (Electrum issue #6170). - Usato in to_date / to_timestamp / __str__ / __repr__ di BalTimestamp. - widgets.py set_value: usa il converter sicuro. - util.py timestamp_minus: stessa protezione inline con clamp a INT32_MAX. I valori entro il 2038 (date assolute normali, durate relative come 90d/5y) producono lo stesso identico risultato di prima. TEST: tests/windows_overflow_test.py riproduce il limite 32-bit di Windows (monkeypatch di datetime.fromtimestamp) e dimostra che senza il fix si ottiene lo stesso OverflowError del log, mentre col fix passa. Verificato anche che il test FALLISCE senza il fix. * docs(it): documenta il fix OverflowError Windows (anno 2038) nel changelog Aggiunge la sezione §13 al CHANGELOG_REFACTOR.md che descrive: - sintomo (schede/menu rotti su Windows dopo riavvio/cambio wallet) - causa vera dal log (datetime.fromtimestamp(NLOCKTIME_MAX) -> OverflowError su time_t 32-bit di Windows) - fix con _safe_fromtimestamp (clamp a INT32_MAX, come Electrum #6170) - test di regressione windows_overflow_test.py Aggiornata anche la cronologia (§12) con PR #3. --------- Co-authored-by: GenSpark AI Developer <ai@genspark.dev>Networking (anti-freeze): - Add check_transactions_parallel: pressing "Check" now contacts will-executors concurrently with a fast-fail timeout (CHECK_TIMEOUT=8, 1 retry) and a global deadline (CHECK_GLOBAL_DEADLINE=30), so a single dead server no longer freezes the "checking transaction" dialog for ~140s (old default 10s x 10 retries). - check_transaction now accepts timeout/max_retries/retry_sleep kwargs. - Rewrite BalWindow.check_transactions_task to use the parallel helper with live progress + an elapsed-time counter "Checking transactions: 2/5 (4s / 30s)". Reliable elapsed-time counters (Xs / DEADLINEs): - Replace the unreliable raw heartbeat thread (whose pyqtSignal emissions were not marshalled and never repainted) with an on_tick callback driven from the CALLING thread in push/ping/check parallel helpers. - Show the maximum wait too (e.g. "3s / 30s") so the user knows when the operation will give up, in the wizard Broadcasting, Ping and Check dialogs. - Expose networking constants (PUSH_*/CHECK_*/DEFAULT_TIMEOUT) as Willexecutors class attributes for a single source of truth in the GUI. GUI: - Add hover tooltips: Wizard ("Wizard - Build your will"), Delivery time (truck), Check Alive (siren), Calendar, Check (refresh). - Reorder the Will toolbar to: Wizard | Delivery time | Check Alive | Calendar | Check; tighten layout margins so it all fits the Will window. Tests: - parallel_ping_test.py: add coverage for check_transactions_parallel (parallel timing, global deadline, on_tick from the calling thread) and static checks that check_transactions_task/loop_push use the parallel helpers + on_tick counter with the 'Xs / Ns' format. Verified: ruff (0 new issues), 182 official tests pass, parallel/smoke/gui_fixes/ windows_overflow/external_zip tests pass.* fix(gui): correct window z-order and lifecycle bugs (B1-B10) Sintomi risolti: - S1: le finestre del plugin sparivano dietro Electrum - S2: alcuni meccanismi funzionavano solo dopo chiusura+pulizia di Electrum La logica di business resta BYTE-IDENTICA (nessuna modifica a bal/core/*): cambiano solo parent, modalità, ciclo di vita, cleanup e presentazione. Nuovo modulo bal/gui/qt/window_utils.py con helper centralizzati: - top_level_of, bring_to_front, stop_thread, show_modal, show_on_top Fix per bug: - B1: self.parent -> self._bal_parent (dialogs/lists/widgets); parent = top_level_of(parent) - B2/B9: .show() -> show_on_top()/show_modal()/bring_to_front() - B3: init a caldo _setup_window() replica load_wallet (niente 'restart Electrum') - B4: chiave finestra stabile _window_key() = id(window) - B5: on_close riscritto (no except:pass, log per-step, reset stato) - B6: BalBlockingWaitingDialog ripristina processEvents() - B7/B8: closeEvent/hideEvent -> stop_thread() (stop+wait) + super() - B10: uso di window.tools_menu (no ricerca per titolo localizzato '&Tools') Test: smoke + gui_fixes (regressione B1-B10) + external_zip tutti verdi. Doc aggiornata: DIAGNOSI_GUI.md marca B1-B10 come FIXED. * fix(gui): do not kill task thread on dialog close (download list regression) The B7/B8 change added stop_thread() to BalDialog.closeEvent/hideEvent. But Electrum's TaskThread.on_done runs cb_done (often self.accept, which closes the waiting dialog) BEFORE cb_result (on_success, which updates the will-executor list). Stopping/joining the thread inside closeEvent therefore tore the thread down before on_success ran, silently dropping the downloaded will-executor list ('Download List' appeared to do nothing). Restore the original safe behavior: the base BalDialog no longer stops the thread on close/hide (matching the original plugin, which deliberately left this commented out). Long-lived dialogs that own a thread still stop it explicitly in their own handlers. Adds a regression test asserting BalDialog.closeEvent/hideEvent never call stop_thread. * fix(gui): restore modal exec for waiting dialog + surface download failures Two changes to fix 'Download List' doing nothing: 1) BalWaitingDialog.exe() now keeps the original application-modal exec() (only adding raise/activate for visibility). The earlier switch to window-modal could interfere with how the TaskThread result (on_success, which populates the will-executor list) is delivered via a queued signal while the modal loop is spinning. 2) BalWindow.download_list now logs how many entries were received and, when the result is empty (the core download_list swallows errors and returns {}), shows a warning to the user instead of failing silently. This makes any future network/parse failure visible in the Electrum log and to the user. Business logic in bal/core/* is unchanged. * fix(gui): show the real download error reason in the warning popup When 'Download List' fails, the core download_list returns {} and the cause was only visible in the Electrum log (which is hard to capture). The GUI now re-issues the same raw request when the result is empty and shows the actual exception/reason in the warning popup (e.g. SSL error, timeout, empty server response). Pure GUI-side diagnostics; bal/core/* logic unchanged. * fix(gui): download will-executor list synchronously like the original ROOT CAUSE: the original plugin's 'Download List' button called Willexecutors.download_list() DIRECTLY on the GUI thread (qt.py: WillExecutorWidget.download_list). The refactor instead routed the button through BalWaitingDialog + TaskThread. Electrum's Network.send_http_on_proxy behaves differently depending on the calling thread, and on the user's setup the TaskThread path timed out ('No response from the server'), while the original direct call worked fine. FIX: WillExecutorWidget.download_list now performs the same direct, synchronous download as the original, updates and saves the list, and shows a warning only on genuine failure. The wizard download path (which the original also ran via TaskThread) is left unchanged. Compared against upstream original source (kaibot/bal-electrum-plugin): send_request, handle_response, download_list core logic are byte-identical; only the GUI call site is restored to the original behaviour. * diag(gui): detailed download diagnostics + hardcoded-URL fallback The will-executor download still times out on the user's setup even with the direct (original-style) GUI-thread call, and the URL/request are byte-identical to the working original. To pinpoint the real cause, the Download List button now: - logs whether a Network instance is present; - tries the configured WELIST_SERVER URL AND falls back to the original hardcoded https://welist.bitcoin-after.life/ endpoint (so a stale/bad config value can't break it); - shows the EXACT URL(s) tried and the precise exception per attempt in the warning popup instead of a generic timeout message. bal/core networking remains unchanged. * diag(gui): add direct-HTTPS control probe to download diagnostics When the Electrum-network download fails, also run a plain urllib HTTPS GET (bypassing Electrum's Network/proxy layer) and show its result in the popup. This distinguishes a real connectivity/DNS/firewall problem from an Electrum-network-state problem, so we can finally pinpoint why the request times out only in this build. * fix(gui): unify wizard + button download on one synchronous path with diagnostics The 'No response from the server (timeout?)' popup was coming from the WIZARD download path in window.py (BalWaitingDialog + TaskThread), which was never switched to the original direct call - only the list button had been. Both paths now share BalWindow.fetch_will_executors_list(): a direct, synchronous GUI-thread download (like the original), trying the configured server then the hardcoded fallback, with full diagnostics (exact URL/error per attempt + a direct-HTTPS control probe) shown in the failure popup. This both fixes the wizard timeout and guarantees the same diagnostic popup ('Details (via Electrum network)' + 'Direct connection test') regardless of which UI entry point is used. * fix(gui): clean up will-executor download (waiting dialog + simple message) Root cause of the 'download not working' reports was environmental (the user's network/ISP was resetting the connection to the IPv6/IPv4 host; a VPN fixes it), NOT a plugin bug. Final cleanup of the diagnostic code: - download_list (button + wizard) again uses BalWaitingDialog + TaskThread so the GUI is not frozen and shows a 'Downloading will-executors list...' dialog. - Keep the configured + hardcoded-fallback server URLs and detailed per-attempt diagnostics, but write them to the Electrum log only. - On failure the user now sees a simple English message explaining it is most likely a connection/firewall issue (a VPN often helps), instead of a technical timeout/exception dump. - Removed the urllib control probe from the user-facing popup. bal/core networking unchanged. --------- Co-authored-by: GenSpark AI Developer <ai@genspark.dev>Aggiunge al CHANGELOG_REFACTOR.md (in italiano) le sezioni che mancavano per coprire il refactoring dall'inizio alla fine rispetto al codice originale Gitea: - §9 Correzioni GUI B1-B10 (z-order, parent, modalita, ciclo di vita) + nuovo modulo window_utils.py - §10 Fix download lista will-executor: difetti GUI corretti (closeEvent/ hideEvent non fermano piu il TaskThread; exe() torna a exec()), percorsi pulsante/wizard unificati, causa vera ambientale (WinError 10054 risolto via VPN), pulizia finale + messaggio errore in inglese - §11 Confronto strutturale finale originale Gitea -> refactor (file/righe) - §12 Cronologia commit su GitHub DIAGNOSI_GUI.md: aggiornato lo stato (B1-B10 mergiati in main, PR #2dd6f677).* fix(gui): voci di menu BAL duplicate/condensate dopo riavvio o cambio wallet Sintomo (Windows 11): dopo aver riavviato Electrum o cambiato wallet, le schede Will/Heirs sparivano dalla tab bar e dal menu, e compariva una voce di menu condensata/illeggibile (icona + testo sovrapposti) sotto il logo di Electrum, accanto a 'Portafogli'. Causa: init_menubar_tools veniva eseguito DUE volte sulla stessa finestra. Con il plugin gia abilitato, al riavvio Electrum invoca sia l'hook init_menubar sia il percorso di init a caldo (init_qt -> _setup_window), entrambi chiamano init_menubar_tools -> addTab/addAction duplicati. Nell'originale init_qt faceva return (chiedendo il riavvio) e quindi i menu venivano creati una sola volta; rimuovendo quel return (fix B3) e' emersa la doppia inizializzazione. Fix: - BalWindow._menubar_initialized: guardia di idempotenza. - init_menubar_tools: se gia inizializzato, esce subito (niente duplicati). - on_close: resetta il flag dopo aver rimosso tab/azioni, cosi la stessa finestra puo essere riusata per un altro wallet. - tests/gui_fixes_test.py: regressione che verifica la guardia in __init__, init_menubar_tools e on_close. Logica di business invariata (nessuna modifica a bal/core/*). * fix(gui): ripristina create_status_bar come no-op (come originale) L'elemento di menu condensato/illeggibile sotto il logo di Electrum era causato dal StatusBarButton aggiunto da create_status_bar. Nell'originale Gitea questo hook aveva un 'return' subito dopo il log, PRIMA di costruire il bottone: era quindi disabilitato di proposito. Durante la pulizia del 'dead code' nel refactoring quel return era stato rimosso, riattivando la creazione del bottone -> elemento icona+testo renderizzato nel punto sbagliato dopo riavvio/cambio wallet. Fix: create_status_bar torna a essere un no-op (return), fedele all'originale. Le impostazioni restano raggiungibili da Strumenti -> Plugin. Regressione: gui_fixes_test verifica che create_status_bar non chiami addPermanentWidget. * fix(core): OverflowError su Windows (anno 2038) che rompeva tab/menu BAL CAUSA VERA (dal log Electrum dell'utente, Windows 11): OverflowError: Python int too large to convert to C int window.py __init__ -> create_heirs_tab -> WillSettingsWidget -> on_locktime_change -> BalTimestamp.to_date -> datetime.fromtimestamp(NLOCKTIME_MAX) Su Windows time_t e' a 32 bit, quindi datetime.fromtimestamp() solleva OverflowError per qualsiasi timestamp oltre il 2038 (es. NLOCKTIME_MAX = 2**32-1 = 4294967295, usato come locktime di default/sentinella). Su Linux 64-bit la stessa chiamata funziona: per questo il bug si vedeva solo su Windows e i test su Linux non lo intercettavano. L'eccezione interrompeva BalWindow.__init__ durante init_menubar/load_wallet, lasciando le schede Will/Heirs e la voce di menu a meta' costruzione -> l'elemento grafico condensato/illeggibile sotto il logo di Electrum. FIX (comportamento invariato per tutti i valori normali): - BalTimestamp._safe_fromtimestamp(): datetime.fromtimestamp con clamp a INT32_MAX in caso di OverflowError/OSError/ValueError, esattamente come la funzione get_max_allowed_timestamp() dell'originale (Electrum issue #6170). - Usato in to_date / to_timestamp / __str__ / __repr__ di BalTimestamp. - widgets.py set_value: usa il converter sicuro. - util.py timestamp_minus: stessa protezione inline con clamp a INT32_MAX. I valori entro il 2038 (date assolute normali, durate relative come 90d/5y) producono lo stesso identico risultato di prima. TEST: tests/windows_overflow_test.py riproduce il limite 32-bit di Windows (monkeypatch di datetime.fromtimestamp) e dimostra che senza il fix si ottiene lo stesso OverflowError del log, mentre col fix passa. Verificato anche che il test FALLISCE senza il fix. * docs(it): documenta il fix OverflowError Windows (anno 2038) nel changelog Aggiunge la sezione §13 al CHANGELOG_REFACTOR.md che descrive: - sintomo (schede/menu rotti su Windows dopo riavvio/cambio wallet) - causa vera dal log (datetime.fromtimestamp(NLOCKTIME_MAX) -> OverflowError su time_t 32-bit di Windows) - fix con _safe_fromtimestamp (clamp a INT32_MAX, come Electrum #6170) - test di regressione windows_overflow_test.py Aggiornata anche la cronologia (§12) con PR #3. --------- Co-authored-by: GenSpark AI Developer <ai@genspark.dev>Networking (anti-freeze): - Add check_transactions_parallel: pressing "Check" now contacts will-executors concurrently with a fast-fail timeout (CHECK_TIMEOUT=8, 1 retry) and a global deadline (CHECK_GLOBAL_DEADLINE=30), so a single dead server no longer freezes the "checking transaction" dialog for ~140s (old default 10s x 10 retries). - check_transaction now accepts timeout/max_retries/retry_sleep kwargs. - Rewrite BalWindow.check_transactions_task to use the parallel helper with live progress + an elapsed-time counter "Checking transactions: 2/5 (4s / 30s)". Reliable elapsed-time counters (Xs / DEADLINEs): - Replace the unreliable raw heartbeat thread (whose pyqtSignal emissions were not marshalled and never repainted) with an on_tick callback driven from the CALLING thread in push/ping/check parallel helpers. - Show the maximum wait too (e.g. "3s / 30s") so the user knows when the operation will give up, in the wizard Broadcasting, Ping and Check dialogs. - Expose networking constants (PUSH_*/CHECK_*/DEFAULT_TIMEOUT) as Willexecutors class attributes for a single source of truth in the GUI. GUI: - Add hover tooltips: Wizard ("Wizard - Build your will"), Delivery time (truck), Check Alive (siren), Calendar, Check (refresh). - Reorder the Will toolbar to: Wizard | Delivery time | Check Alive | Calendar | Check; tighten layout margins so it all fits the Will window. Tests: - parallel_ping_test.py: add coverage for check_transactions_parallel (parallel timing, global deadline, on_tick from the calling thread) and static checks that check_transactions_task/loop_push use the parallel helpers + on_tick counter with the 'Xs / Ns' format. Verified: ruff (0 new issues), 182 official tests pass, parallel/smoke/gui_fixes/ windows_overflow/external_zip tests pass.C2 - 'Editable dates' option (default OFF): - new EDITABLE_DATES config; checkbox in settings dialog - WillSettingsWidget.apply_editable_dates() re-locks/unlocks the delivery-time and check-alive fields, called from update_all() so the toggle takes effect immediately (same mechanism as Hide Invalidated) - EDITABLE_DATES included in the Reset-to-defaults list C3 - RAW date box narrowed to ~1/3 (input box 6 chars); the trailing label that shows the computed absolute date kept at 10 chars (fixed a truncation regression) C4 - settings dialog: (a) bold red warning at the top (b) 'Reset setting' button restoring all 7 dialog settings to defaults (single source of truth: BalConfig.default), not touching wills (c) bold 'Support: bitcoin-after.life' link (opens via webopen) C5 - tooltips: calendar 'Export reminder dates to your calendar (.ics)', fee field 'Mining fee rate in sat/vByte used for the will transactions', fee '丰' icon 'Miner fee, click for more information' (tooltip font scoped to QPushButton so it matches the other tooltips) C6 - Locktime column rendered in bold in the will list tests/test_group_c_settings.py: 4 new tests (EDITABLE_DATES default/toggle, reset restores all dialog settings incl. EDITABLE_DATES, reset leaves unrelated config untouched). Full suite: 210 passed.D1 - number of reminders (default 3, max 5): - new NUM_REMINDERS config in plugin_base.py - new BalSpinBox widget bound to a BalConfig - new pure helper compute_reminder_offsets(days, count): reminders spread uniformly across the check-alive period, always before the deadline (offset >= 1), at most one per available day, de-duplicated, earliest first (e.g. (30,3)->[30,16,1], (2,3)->[2,1], (1,3)->[1], (0,3)->[]) - create_alarms() rewritten to use it and emit one VALARM per offset with a DESCRIPTION reminder text - 'Number of reminders' spin box (range 1..5) added to the settings dialog and to the Reset list D1b - save-only .ics (ask where, default to Desktop): - BalCalendar.desktop_dir() helper (~/Desktop with home fallback) - open_or_save_calendar() no longer opens the file with a calendar app; it always shows a 'save as' dialog (.ics filter, default will_event.ics, starting on the Desktop) and copies the file there, then confirms. Identical behaviour on Windows/Linux/macOS. save_to_cwd -> save_ics_to. Follow-up: removed the now-unused 'Calendar App' field from the settings dialog (and from the Reset list). CALENDAR_APP config and open_with_default_app left in place (unused) to avoid unrelated changes. D2 intentionally skipped (per user request). tests/test_group_d_alarms.py: 7 new tests (NUM_REMINDERS default/change and all distribution rules). Full suite: 217 passed.Add tests/test_group_e_mock_giovanna7.py (22 GUI-free tests) driven by a self-contained fake wallet named giovanna7, covering four areas: - E1 calendar/.ics: reminder-offset distribution (compute_reminder_offsets), VALARM/TRIGGER shape (TRIGGER;RELATED=END:-P{n}D), iCalendar escaping, and temporary .ics file creation. - E2 inheritance/states: loading/adding/removing heirs (Heirs), WillItem status transitions (VALID->COMPLETE, INVALIDATED clears VALID, PUSHED clears PUSH_FAIL), Will.only_valid, and heir-change detection. - E3 connectivity: stubbed get_info_task proving ping_servers_parallel runs concurrently, fires on_each once per server with the right ok flag, and writes results back; plus empty-mapping no-op. - E4 will-executor: is_selected default/set, get_willexecutor_transactions filtering (only VALID+COMPLETE+not-PUSHED+selected; force re-includes PUSHED), and compute_id. CHANGELOG: add entry #9. ruff clean; full suite 239 passed.Show 'No active will-executor servers for {chain}' when the server responds with no data, and 'Could not reach the configured welist server' only for actual connection errors.Self-review approved
Approved for merge
Pull request closed