* 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>
23 KiB
BAL — Resoconto del refactoring (per l'autore originale)
Questo documento elenca tutte le modifiche apportate al plugin BAL
(Bitcoin After Life) rispetto alla versione originale 0.2.8.
Principio guida: refactoring conservativo e a comportamento invariato (Approccio A). La logica di business è stata mantenuta byte-identica dove possibile; sono cambiati soprattutto la disposizione dei file e gli import. Nessuna riscrittura algoritmica.
Ambiente di verifica: Electrum 4.7.2 + PyQt6 (l'ultima release stabile
che espone json_db.register_dict).
1. Riorganizzazione della struttura (separazione logica / GUI)
Il problema principale segnalato era che la logica e la grafica erano
mescolate, in particolare in un unico file qt.py da 4131 righe.
Struttura PRIMA (flat, 7 file)
BAL/
├── __init__.py (vuoto, 0 righe)
├── bal.py (243) logica + plugin base
├── util.py (533) helper
├── heirs.py (791) modello eredi + costruzione tx
├── will.py (927) modello will/WillItem
├── willexecutors.py (374) networking will-executor
├── qt.py (4131) TUTTA la GUI + il Plugin in un solo file
└── bal_resources.py (14)
Struttura DOPO (core/ vs gui/)
bal/
├── manifest.json metadati conformi allo standard
├── qt.py shim di caricamento (re-export di Plugin)
├── __init__.py docstring di architettura + __version__
├── core/ LOGICA senza dipendenze Qt
│ ├── util.py (ex util.py)
│ ├── plugin_base.py (ex bal.py)
│ ├── heirs.py (ex heirs.py)
│ ├── will.py (ex will.py)
│ └── willexecutors.py (ex willexecutors.py)
└── gui/qt/ PRESENTAZIONE PyQt6
├── theme.py (59) mappatura stato → colore
├── common.py (155) import condivisi + helper GUI
├── widgets.py (782) widget "foglia"
├── calendar.py (80) BalCalendar
├── dialogs.py (1127) finestre di dialogo
├── lists.py (957) viste ad albero (eredi/preview/executor)
├── window.py (952) controller GUI per-wallet (BalWindow)
└── plugin.py (273) classe Plugin (@hook Electrum → GUI)
Il file qt.py da 4131 righe è stato suddiviso per responsabilità. I
corpi delle classi sono stati copiati verbatim (riga per riga) per non
toccare la logica delicata delle transazioni di eredità.
Mappa: dove sono finite le 40 classi/funzioni di qt.py
| Classe/funzione (riga orig.) | Nuovo modulo |
|---|---|
Plugin (67) |
gui/qt/plugin.py |
shown_cv (317) |
gui/qt/common.py |
BalWindow (330) |
gui/qt/window.py |
add_widget (1257) |
gui/qt/common.py |
ClickableLabel (1263) |
gui/qt/widgets.py |
BalTxFeesWidget (1271) |
gui/qt/widgets.py |
_LockTimeEditor (1340) |
gui/qt/widgets.py |
BalTimeEditWidget (1374) |
gui/qt/widgets.py |
TimeRawEditWidget (1508) |
gui/qt/widgets.py |
LockTimeRawEdit (1527) |
gui/qt/widgets.py |
LockTimeDateEdit (1605) |
gui/qt/widgets.py |
ThresholdTimeWidget (1644) |
gui/qt/widgets.py |
LockTimeWidget (1664) |
gui/qt/widgets.py |
WillSettingsWidget (1683) |
gui/qt/widgets.py |
PercAmountEdit (1818) |
gui/qt/widgets.py |
BalDialog (1883) |
gui/qt/dialogs.py |
BalWizardDialog (1913) |
gui/qt/dialogs.py |
BalWizardWidget (2002) |
gui/qt/dialogs.py |
BalWizardHeirsWidget (2068) |
gui/qt/dialogs.py |
BalWizardWEDownloadWidget (2103) |
gui/qt/dialogs.py |
BalWizardWEWidget (2190) |
gui/qt/dialogs.py |
BalWizardLocktimeAndFeeWidget(2207) |
gui/qt/dialogs.py |
BalWaitingDialog (2224) |
gui/qt/dialogs.py |
BalBlockingWaitingDialog (2285) |
gui/qt/dialogs.py |
BalLineEdit (2304) |
gui/qt/widgets.py |
BalTextEdit (2312) |
gui/qt/widgets.py |
BalCheckBox (2320) |
gui/qt/widgets.py |
BalBuildWillDialog (2335) |
gui/qt/dialogs.py |
HeirListWidget (2858) |
gui/qt/lists.py |
PreviewList (3059) |
gui/qt/lists.py |
WillDetailDialog (3445) |
gui/qt/dialogs.py |
WillWidget (3545) |
gui/qt/widgets.py |
WillExecutorListWidget (3637) |
gui/qt/lists.py |
WillExecutorWidget (3873) |
gui/qt/lists.py |
WillExecutorDialog (3982) |
gui/qt/dialogs.py |
CheckAliveError (4018) |
gui/qt/common.py |
log_error (4028) |
gui/qt/common.py |
export_meta_gui (4043) |
gui/qt/common.py |
BalCalendar (4066) |
gui/qt/calendar.py |
2. Rimozioni (codice morto / debug) — comportamento invariato
Tutte le rimozioni seguenti sono state verificate come non utilizzate o puramente di debug, quindi non alterano il comportamento del plugin.
-
util.py→core/util.py: rimossi tre helper di debug usati solo per stampe a console:print_var()(orig. riga 439)print_utxo()(orig. riga 474)print_prevout()(orig. riga 486)
-
bal.py→core/plugin_base.py: rimossa la funzione stub vuotaget_will_settings(x)(orig. righe 12-14):def get_will_settings(x): # print(x) pass⚠️ Verificato: non era riferita da nessun
register_dict— i treregister_dictusanotuple,dict,lambda x: x. Quindi era codice morto. La funzione usataget_will(x)è stata mantenuta identica. -
will.py(WillItem) → spostato ingui/qt/theme.py: il metodoWillItem.get_color()(orig. riga 852) restituiva colori esadecimali — è logica di presentazione, non di dominio. È stato spostato fuori dal modello e trasformato nella funzionestatus_color(will_item)ingui/qt/theme.py. Verificato byte-identico su tutte le combinazioni di stato (stessa catena diget_status(...), stessi codici colore).
3. Cambi di import (necessari per la nuova struttura)
Gli import sono stati aggiornati da "flat" a "a package". Esempi:
| Prima | Dopo |
|---|---|
from .bal import BalPlugin |
from .plugin_base import BalPlugin (in willexecutors) |
from .util import Util |
from .util import Util (invariato, ora dentro core/) |
(in qt.py) from .bal import ... |
i moduli GUI importano da ...core.X |
- Aggiunti
from .common import _, _loggernei moduli GUI, perchéimport *non esporta i nomi che iniziano con underscore. - Aggiunti 3 import "lazy" (dentro le funzioni) in
dialogs.pyper spezzare il ciclodialogs ↔ lists(lists importaBalBuildWillDialogda dialogs).
La logica interna dei metodi non è stata toccata: prepare_transactions(),
buildTransactions() ecc. sono verbatim.
4. Packaging conforme allo standard Electrum
manifest.json reso conforme a https://plugins.electrum.org/developers.html :
| Campo | Prima | Dopo |
|---|---|---|
name |
"BAL" |
"bal" (minuscolo = nome dir) |
version |
(assente, era solo nella description) | "0.2.8" |
description |
con <br> HTML |
testo pulito |
licence |
(assente) | "MIT" |
fullname/author/available_for/icon |
presenti | invariati |
__init__.py(era vuoto): ora contiene la docstring di architettura e__version__ = "0.2.8".- Portati nel package:
LICENSE,VERSION,README.md,bal_resources.py, e la cartellawallet_util/(invariata).
5. CORREZIONE BUG: caricamento come plugin esterno (.zip)
Durante i test su Electrum 4.7.2 portable per Windows sono emersi due problemi reali nel caricare il plugin come plugin esterno da .zip:
Bug 5a — ModuleNotFoundError: No module named 'electrum_external_plugins'
- Causa: Electrum carica i plugin esterni da zip sotto il package sintetico
electrum_external_plugins.bal, ed esegue solo l'__init__del package e il moduloqt. Non registra il package radice sintetico né i sotto-package annidati (gui,gui.qt). Un semplicefrom .gui.qt.plugin import Pluginfallisce risalendo ai parent mancanti. - Fix:
qt.pyora è uno shim resiliente che (1) rileva a runtime il proprio nome di package (__package__), (2) ricostruisce insys.modulesgli eventuali package padre mancanti, (3) importaPluginconimportlib.import_module. Funziona sia come plugin interno (electrum.plugins.bal) sia esterno (electrum_external_plugins.bal).
Bug 5b — zlib.error: Error -5 ... incomplete or truncated stream
- Causa: alcune build portable di Electrum su Windows non riescono a
decomprimere con
zipimportarchivi che contengono voci di directory o compressione non standard. - Fix: aggiunto
build_zip.py, che genera un archivio "zipimport-friendly": solo file (nessuna voce di directory), DEFLATE standard, ordine deterministico (SHA-256 riproducibile), escludendo__pycache__/*.pyc. Stampa anche l'hash SHA-256 per verificare l'integrità del download.
6. Test aggiunti
tests/smoke_test.py— verifica import + comportamento di base (BalTimestamp, helper diUtil, costantiHEIR_*, stati diWillItem, hook delPlugin).tests/external_zip_test.py— riproduce fedelmente la sequenza di caricamento di un plugin esterno da zip di Electrum (regressione per il Bug 5a/5b).
Tutti i test passano sotto Electrum 4.7.2 + PyQt6.
7. Riepilogo: cosa NON è cambiato
- La logica di costruzione delle transazioni (
heirs.py,will.py). - I valori e i tipi di
json_db.register_dict(...). - I codici colore degli stati (solo spostati in
theme.py). - L'algoritmo di tutte le classi GUI (copiate verbatim).
- Il formato dei dati salvati nel wallet.
8. Note / raccomandazioni
- Il plugin richiede Electrum 4.7.2:
json_db.register_dictè stato rimosso nelle versioni successive (master), dove andrebbe sostituito constored_dict.register_name. Valutare un adeguamento se si vuole supportare Electrum più recente. - Prima del rilascio è consigliata una prova end-to-end in una sessione Electrum reale (preferibilmente su testnet), oltre agli smoke test.
9. CORREZIONI GUI — finestre e ciclo di vita (B1-B10)
Dopo il refactoring di struttura sono stati corretti dieci difetti grafici e
di ciclo di vita delle finestre, già presenti nel codice originale. La logica
di business è rimasta byte-identica (nessuna modifica a bal/core/*): sono
cambiati solo presentazione, parent, modalità, z-order, ciclo di vita e
cleanup delle finestre Qt.
Sintomi segnalati dall'utente, ora risolti:
- (S1) le finestre del plugin sparivano dietro la finestra di Electrum;
- (S2) alcuni meccanismi funzionavano solo dopo aver chiuso e riavviato Electrum.
| ID | Problema (presente nell'originale) | Correzione applicata |
|---|---|---|
| B1 | self.parent = parent sovrascriveva il metodo parent() di Qt, rompendo la gerarchia delle finestre |
rinominato in self._bal_parent (in dialogs.py, lists.py, widgets.py); il parent reale passa da top_level_of(parent) |
| B2 | dialoghi aperti con .show() non modale → finivano sotto la finestra principale |
sostituiti con show_on_top() / show_modal() e parent corretto |
| B3 | messaggio "Please restart Electrum to activate the BAL plugin": il plugin si attivava solo dopo riavvio | inizializzazione a caldo con _setup_window() che replica load_wallet — niente più riavvio |
| B4 | chiave del dizionario finestre usava il metodo winId invece del valore |
chiave stabile _window_key() basata su id(window) |
| B5 | on_close ingoiava tutti gli errori con except: pass |
riscritto: niente except:pass, log per ogni passo, reset pulito dello stato |
| B6 | BalBlockingWaitingDialog bloccava il thread della GUI (processEvents commentato) |
ripristinato processEvents() → GUI reattiva durante l'attesa |
| B7 | closeEvent/hideEvent con cleanup del thread commentato |
gestione esplicita di closeEvent/hideEvent + chiamata a super() |
| B8 | closeEvent incompleto in alcuni dialog |
gestione uniforme dello stato di chiusura |
| B9 | show()+raise_() senza activateWindow() né modalità → finestra non in primo piano |
bring_to_front() = raise_() + activateWindow() |
| B10 | gestione multi-wallet / multi-finestra fragile; menu cercato per titolo &Tools |
uso dell'API ufficiale window.tools_menu |
Nuovo modulo: gui/qt/window_utils.py (119 righe)
Gli helper per la gestione delle finestre sono stati centralizzati in un unico modulo, così la stessa logica non viene duplicata nei vari dialog:
top_level_of(widget)— risale alla finestra di primo livello corretta da usare come parent;bring_to_front(window)—raise_()+activateWindow()per portare in primo piano;stop_thread(thread)— stop+wait sicuro di unTaskThread;show_modal(dialog)— apertura modale corretta (exec());show_on_top(window)— apertura non modale ma sopra le altre finestre.
gui/qt/common.py importa questi helper e li rende disponibili al resto della
GUI.
10. CORREZIONE BUG: download lista will-executor
Dopo l'installazione del pacchetto con le correzioni GUI, l'utente ha segnalato che il comando "download list" dei will-executor non scaricava più la lista.
Indagine
Il codice di rete (core/willexecutors.py: send_request, handle_response,
download_list, initialize_willexecutor) è stato confrontato riga per riga
con l'originale Gitea ed è risultato byte-identico (l'unica differenza è il
parametro aggiuntivo welist_server in download_list, retro-compatibile).
Durante l'indagine sono comunque emersi e stati corretti due difetti reali introdotti dalle correzioni GUI, che potevano "perdere" il risultato del download:
BalDialog.closeEvent/hideEventfermavano ilTaskThread. In ElectrumTaskThread.on_doneeseguecb_done(cioèself.accept, che chiude il dialog) prima dicb_result(cioèon_success, che aggiorna la lista). Fermare il thread alla chiusura del dialog scartava quindi il risultato appena scaricato. → I due metodi sono stati riportati a non fermare il thread (con commento esplicativo nel codice).BalWaitingDialog.exe()usava una modalità sbagliata (show_modal/WindowModal). → Ripristinato l'originaleself.exec(), aggiungendo primabring_to_front(self)per garantire il primo piano.
Inoltre i percorsi del pulsante e del wizard (che prima scaricavano in
modi diversi e con messaggi diversi) sono stati unificati in un unico
helper fetch_will_executors_list, eseguito dentro il worker del TaskThread.
Causa vera del mancato download: ambientale, NON del plugin
Una probe di controllo con urllib che bypassava completamente Electrum
falliva ugualmente con WinError 10054 ("connection forcibly closed by remote
host"): segno che la rete/ISP dell'utente resettava la connessione HTTPS
verso welist.bitcoin-after.life. La conferma definitiva: attivando una VPN
il download è andato a buon fine.
L'originale "sembrava" funzionare perché spedisce comunque un will-executor di
default già incorporato (https://we.bitcoin-after.life), quindi la lista
non risultava mai del tutto vuota anche senza un download riuscito.
Pulizia finale (scelta dall'utente — "Opzione 1")
-
Finestra di attesa non bloccante mantenuta (
BalWaitingDialog), così la GUI non si congela durante il download. -
Fallback dell'URL: prima l'URL configurato (
WELIST_SERVER), poi quello hardcodedhttps://welist.bitcoin-after.life/. -
Diagnostica dettagliata spostata nei soli log (rimossa la probe
urllibdall'interfaccia). -
Messaggio d'errore semplice per l'utente, in inglese (
DOWNLOAD_FAILED_MESSAGE):"Could not download the will-executors list. This is usually caused by your internet connection or a firewall, not by the plugin. Please check your connection (a VPN often helps) and try again."
File toccati (solo presentazione/GUI, logica invariata)
gui/qt/window.py— helper condivisofetch_will_executors_list,download_listconTaskThread+BalWaitingDialog, costanteDOWNLOAD_FAILED_MESSAGE.gui/qt/lists.py—WillExecutorWidget.download_listinstradato sul percorso condiviso conon_successche aggiorna/salva la lista.gui/qt/dialogs.py—BalDialog.closeEvent/hideEventnon fermano più il thread;BalWaitingDialog.exe()torna aself.exec()+bring_to_front.tests/gui_fixes_test.py— asserzione di regressione: verifica checloseEvent/hideEventnon contenganostop_thread(per non reintrodurre il bug che scartava il download).
11. Confronto strutturale finale (originale Gitea → refactor)
Conteggio file .py (escluse cartelle generate):
| Originale (Gitea) | righe | → | Refactor (bal/) |
righe |
|---|---|---|---|---|
__init__.py |
1 | → | __init__.py |
37 |
bal.py |
161 | → | core/plugin_base.py |
351 |
util.py |
1051 | → | core/util.py |
614 |
heirs.py |
792 | → | core/heirs.py |
806 |
will.py |
903 | → | core/will.py |
938 |
willexecutors.py |
547 | → | core/willexecutors.py |
390 |
qt.py (monolite GUI) |
3777 | → | suddiviso in gui/qt/* (vedi sotto) |
— |
bal_resources.py |
14 | → | bal_resources.py |
14 |
wallet_util/*.py |
275 | → | wallet_util/*.py (invariati) |
280 |
Suddivisione del vecchio qt.py (3777 righe) nei moduli GUI:
| Modulo refactor | righe | Contenuto |
|---|---|---|
gui/qt/plugin.py |
303 | classe Plugin (@hook Electrum → GUI) |
gui/qt/window.py |
1048 | BalWindow (controller per-wallet) |
gui/qt/dialogs.py |
1155 | finestre di dialogo + wizard |
gui/qt/lists.py |
964 | viste ad albero (eredi/preview/executor) |
gui/qt/widgets.py |
782 | widget "foglia" |
gui/qt/common.py |
157 | import condivisi + helper |
gui/qt/window_utils.py |
119 | helper finestre (NUOVO — vedi §9) |
gui/qt/calendar.py |
80 | BalCalendar |
gui/qt/theme.py |
59 | mappatura stato → colore |
gui/qt/__init__.py |
17 | init package GUI |
Le differenze nei conteggi di righe rispetto all'originale derivano da: riformattazione/commenti, separazione degli import per modulo, e spostamento di funzioni tra
util.py/bal.pye i nuovi moduli. Gli algoritmi non sono stati modificati.
12. Cronologia delle modifiche su GitHub
4198a51— import iniziale del refactor strutturale (v0.2.8): separazionecore/(logica) vsgui/qt/(presentazione), packaging conforme, fix caricamento zip esterno, smoke test (sezioni §1-§8).d56fa36— questo changelog del refactoring (in italiano).4806997—DIAGNOSI_GUI.md: diagnosi dei bug GUI di z-order e ciclo di vita (Fase A).dd6f677(PR #2, squash) — correzioni GUI B1-B10 + fix download lista will-executor +window_utils.py+ test di regressione (sezioni §9-§10).- PR #3 — fix OverflowError su Windows (anno 2038) che rompeva le schede Will/Heirs e la voce di menu (sezione §13).
13. CORREZIONE BUG: OverflowError su Windows (limite anno 2038)
Sintomo (Windows 11)
Dopo aver riavviato Electrum o cambiato wallet, le schede Will e Heirs sparivano e compariva una voce di menu condensata/illeggibile (icona + testo sovrapposti) sotto il logo di Electrum, accanto a Portafogli. Su Linux il problema non si manifestava.
Causa vera (dal log di Electrum dell'utente)
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)
NLOCKTIME_MAX = 2**32 - 1 = 4294967295viene usato come locktime di default/sentinella.- Su Windows
time_tè a 32 bit, quindidatetime.fromtimestamp(ts)sollevaOverflowErrorper qualsiasi timestamp oltre il 2038. - Su Linux 64-bit la stessa chiamata funziona: ecco perché il bug si vedeva solo su Windows e i test su Linux non lo intercettavano.
- L'eccezione interrompeva
BalWindow.__init__duranteinit_menubar/load_wallet, lasciando le schede Will/Heirs e la voce di menu a metà costruzione → l'elemento grafico condensato/illeggibile sotto il logo.
Nota: i due primi tentativi di correzione (status-bar no-op e idempotenza di
init_menubar_tools) non centravano la causa; sono stati comunque mantenuti perché innocui e leggermente migliorativi, ma il vero colpevole era questo crash a monte.
Fix (comportamento invariato per tutti i valori normali)
BalTimestamp._safe_fromtimestamp():datetime.fromtimestampcon clamp a INT32_MAX (anno 2038) in caso diOverflowError/OSError/ValueError, esattamente come la funzioneget_max_allowed_timestamp()dell'originale (workaround per Electrum issue #6170).- Usato in
to_date/to_timestamp/__str__/__repr__diBalTimestamp. gui/qt/widgets.py(set_value): usa il converter sicuro.core/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.pyriproduce il limite 32-bit di Windows (monkeypatch didatetime.fromtimestamp) e dimostra che senza il fix si ottiene lo stessoOverflowErrordel log, mentre con il fix passa. Verificato anche che il test fallisce senza il fix.
Confermato dall'utente: "si ora funziona".