Commit Graph

42 Commits

Author SHA1 Message Date
693479b0da willexecutors: fee-bound is_valid (extremes allowed), is_selected flag-only; fix merge_will crash on missing date_to_check; DNSSEC async, ical folding, pyright/ruff cleanup; refresh karen7/samanta7 fixtures 2026-08-01 22:08:53 -04:00
30a5720ceb BalWindow/plugin: history persistence, will import/merge, sig tracking, local-spender fixes 2026-08-01 17:21:36 -04:00
08394f4868 lint: ruff cleanup pass across bal/ and tests/
- Sort imports and fix pyproject ruff config (per-file ignores for
  intentional Qt/core exceptions)
- Mark Heirs.validate_* helpers as @staticmethod
- Clean up dead code, rename shadowing vars, use raise ... from
- Add AGENTS.md with env/lint/test/release guidance
2026-07-31 16:03:17 -04:00
649910e599 Add OP_RETURN support for heirs
Allow heirs to produce an OP_RETURN output instead of a regular BTC
payment. An address prefixed with OP_RETURN: carries hex data (max 80
bytes); such heirs always have amount 0 and are excluded from amount
calculations. GUI dialog shows a message field with the decoded text,
the heir list displays the decoded message, and the OP_RETURN output
is emitted with zero value in inheritance transactions.
2026-07-30 22:06:49 -04:00
fb797f31e3 fix version lookup for internal plugin install; silence JSON parse debug noise 2026-07-30 18:53:43 -04:00
9bf088b7ff Add Willexecutors.is_valid + grey italic styling for invalid executors 2026-07-30 18:11:29 -04:00
4a9299d85b add make-release.sh, svatantrya.asc, and update HANDOFF.md
- make-release.sh: automated release script (tests, lint, build, GPG sign, SHA-256, Gitea)
- svatantrya.asc: PGP public key for release verification
- HANDOFF.md: updated release workflow documentation
- willexecutors.py: input validation for addresses, fees, and API responses
- manifest.json: fixed version back to 0.6.1
- test_core_plugin_base.py: updated baltx_fees default
2026-07-22 21:07:32 -04:00
4b0af6f4bf v0.6.1: read version from manifest.json (single source of truth)
Read the plugin version at runtime from bal/manifest.json via
importlib.resources (zip-safe, works from inside a zip on Windows).
Removes the four-file duplication (VERSION, __init__.py, plugin_base.py,
manifest.json) and deletes the bal/VERSION file.

- bal/core/plugin_base.py: new get_version() function, BalPlugin.version
  is now a @property, removed hardcoded __version__
- bal/__init__.py: removed __version__, replaced with a comment
- bal/core/willexecutors.py: user-agent uses get_version()
- bal/gui/qt/dialogs.py, widgets.py: call sites use .version property
- tests/test_version_source.py: 7 tests incl. zip-safe subprocess check
- HANDOFF.md: updated to single-source-of-truth documentation
- CHANGELOG.md: entries 43-46
- bal/manifest.json: version bumped to 0.6.1

PR #4 (branch bitcoinafterlife-patch-5).
2026-07-22 11:19:18 -04:00
7722272c39 v0.5.18: Tor-gated onion download, crash fix, clearer Tor timeout message 2026-07-16 12:30:30 +00:00
e4bcc4f911 v0.5.16: skip .onion will-executors from download when not on Tor 2026-07-16 09:37:10 +00:00
62502263fd v0.5.15: sync version in plugin_base 2026-07-15 12:08:13 +00:00
df6b5e3e9a v0.5.13: Electrum 4.8.0 compatibility (register_dict -> register_name shim 2026-07-14 10:50:08 +00:00
745489f616 v0.5.11: Electrum 4.8.0 compatibility (register_dict -> register_name shim) 2026-07-13 21:53:51 +00:00
cd2278c2b8 v0.5.10: sync version in plugin_base 2026-07-05 09:06:53 +00:00
2cb39f1a6d v0.5.2: sync version in plugin_base 2026-07-04 10:07:53 +00:00
346164207f v0.5.1: sync version in plugin_base 2026-07-04 08:12:25 +00:00
donkey-ai
ca874d8e93 v0.5.4 2026-07-02 00:29:26 +02:00
kaibot
868674ece2 baltx_fees default 100 -> 20 2026-06-30 06:23:44 -04:00
kaibot
aaa8658aa0 fix: default baltx_fees 100 -> 50 2026-06-30 06:21:58 -04:00
kaibot
aa03c12aab v0.5.0 2026-06-30 04:33:24 -04:00
kaibot
6d568bf304 Fix: NoneType error in normalize_will when others_inputs is None
When deleting old transactions and clicking Check, task_phase1 would fail
with 'NoneType object has no attribute get' error in WillItem.normalize_locktime.

Root cause: In Will.normalize_will (will.py:150), the parameter others_inputs
defaults to None. At line 151, a local variable 'others_input' is created with
a safe default value (empty dict). However, lines 179 and 184 still used the
original 'others_inputs' parameter instead of the safe local variable, causing
the error when calling .get() on None.

Fix: Use the safe 'others_input' variable instead of the raw parameter in
lines 179 and 184.

Also added:
- Traceback logging to on_error_phase1 so future errors include full call stack
- Debug logging in update_all to log willitems state before processing
- Comprehensive test (test_e5_build_with_real_wallet_heirs_and_utxos) that
  reproduces the exact scenario from the user's karen7 wallet
2026-06-29 12:03:24 -04:00
kaibot
7a58c69533 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
2026-06-29 00:18:42 -04:00
kaibot
b8e6c240be feat(willexecutors): differentiate empty server response from network error
Raise NoServersForChainError when the welist server responds but returns
no data for the requested chain, showing a chain-specific message instead
of the generic network error.
2026-06-28 23:56:30 -04:00
donkey-ai
33e8a7533d v0.4.8: seven UX fixes for the BAL inheritance plugin
- #04: Raw/Date selector now reappears on the WILL/HEIR tabs when switching to
  ADVANCED (and when a raw value is pushed from the wizard), keeping the current
  value/editor. New BalTimeEditWidget.apply_user_type_visibility() wired into
  WillSettingsWidget for both the locktime and check-alive boxes.
- #3: Building Will report window minimum height 500 -> 450 px.
- #5: 'User Type' setting moved to the bottom, above 'Rebroadcast transactions'.
- #6: enabling ADVANCED now requires typing 'at My Risk' (case-insensitive);
  wrong phrase or cancel reverts to BASIC. QInputDialog added to common import.
- #7a: on CHECK with an emptied wallet, an extra reassuring line is shown on
  'Checking your will': 'Inheritance already executed (on blockchain)' in GREEN
  (CONFIRMED) or 'Inheritance in mempool (waiting confirmation)' in ORANGE
  (MEMPOOL). New _executed_inheritance_status() helper.
- #7b: 'Balance is too low, or CheckAlive is in the past. Skipped' (space added)
  recoloured to ORANGE instead of red.
- Reset button renamed 'Reset setting' -> 'Reset to Default Setting'.
- Added tests/test_group_h_v048.py (8 tests). Version 0.4.7 -> 0.4.8.
  Full suite: 266 passed; ruff clean; CHANGELOG #24; memory updated (#04 done).
2026-06-28 23:02:40 -04:00
donkey-ai
646a33f2f5 feat(v0.4.7): report area 500px, heirs one-per-line, wizard line breaks, ALL-DUST guard
Owner-approved changes after testing v0.4.6, plus accumulated v0.4.x work,
task-tracking notes, and an updated project HANDOFF document.

The four v0.4.7 changes:

1. Report area (BalBuildWillDialog) opens 500px tall (min) up to 700px (max),
   then the scrollbar takes over. Previously it opened ~140px (too short).

2. Heirs are listed ONE per line again (green, bold) in _build_success_report,
   reverting the v0.4.6 single-line form. Heir names can be long and the report
   now scrolls, so compression is no longer needed.

3. Two wizard texts get an explicit line break: after "(or backup)" in the date
   hint and after "miner fees" in the fee note (widgets.py).

4. ALL-DUST guard: when EVERY heir's share is below the Bitcoin dust limit, the
   inheritance would pay nobody. Heirs.prepare_lists now raises
   HeirAmountIsDustException at the end (where all heirs across all locktimes
   are known with their final dust state), and dialogs.task_phase1 shows a clear
   RED message and stops without building/signing/checking. A mix of dust +
   valid heirs keeps building normally. The guard is intentionally in
   prepare_lists, NOT prepare_transactions (which only sees the lowest locktime
   and would false-positive). HeirAmountIsDustException is imported in common.py.

Tests: 3 new tests in test_core_heirs_extra.py pin the dust behaviour (all-dust
raises; mixed continues; multi-locktime continues). Full suite: 258 passed.
ruff: no new errors. Version bumped 0.4.6 -> 0.4.7 (4 files). CHANGELOG #23.

Also adds/updates HANDOFF.md so any future AI (Claude or another model) can
resume the project with full context (rules, layout, build/test/lint, dust
logic, git flow), and records the task-tracking notes in
.agent_memory_tasks.md.
2026-06-28 23:02:25 -04:00
donkey-ai
5b707e647b feat(bal): UI batch v0.3.9 + expired-will invalidate regression fix
UI batch (TASK A/B/C/D):
- (A) Clearer 'Will expired' message: shortened will id (8+8 chars) and
  readable UTC date instead of raw UNIX timestamp; shown in WARNING colour
  (orange) instead of ERROR (red) in the wizard, as it is an expected step.
- (A2) Split the orange message onto two lines via <br> (rendered as HTML by
  msg_warning) to avoid an overly long single line.
- (B) History tab labels (text only): inheritance txs -> 'BAL Inheritance
  transaction'; invalidate tx -> 'BAL Invalidate transaction'. Colours left
  to Electrum defaults.
- (C) New 'No will-executor TX' checkbox in plugin settings, bound to the
  existing NO_WILLEXECUTOR config (default ON, kept in sync with the wizard),
  with help text, and included in the settings reset action.
- (D) Wizard button label 'Create your will' -> 'Build Your Will'.

Regression fix (A3):
- When an heir was added to an already-expired will via the wizard, the
  automatic invalidation no longer triggered. Root cause: the inner
  check_will() after build_will() raised WillExpiredException and the handler
  returned (False, None), which never invalidated.
- Auto-opening the invalidation tx window from the closing wizard proved
  unreliable (the window ended up behind the main wallet on some window
  managers; a re-check loop could ask to invalidate repeatedly before the tx
  reached the mempool). The robust fix: close the wizard and show a clear
  instruction popup guiding the user to run 'Tools -> Invalidate' and then
  press 'Check' to finish the will. Reasoning documented in the code.

Version bumped to 0.3.9 (manifest.json, __init__.py, plugin_base.py, VERSION).
CHANGELOG entry #15 updated. Verified: py_compile OK, ruff clean (no new
errors), full test suite 239 passed.
2026-06-28 23:02:11 -04:00
donkey-ai
ce3a463a5e docs(bal): rewrite CHECK ALIVE help text (DATA/RAW modes) + fix two typos (v0.3.8)
Rewrite the CHECK ALIVE help popup (ThresholdTimeWidget.help_text) to explain
both modes, keeping the <b>CHECK ALIVE</b> title bold:
- DATA mode: set the check-alive date; on wallet open, if the date has passed
  the plugin asks whether to postpone the inheritance.
- RAW mode: when less than this time is missing, ask to invalidate; failing to
  invalidate delivers the transactions to the heirs; kept the d/y suffix help.

Fix two typos within this help text: 'less then' -> 'less than',
'currrent' -> 'current'.

Bump version to 0.3.8 across the four version files. CHANGELOG entry #14.
2026-06-28 23:02:03 -04:00
donkey-ai
50129d3b77 feat(bal): export calendar .ics as separate reminder events (v0.3.7)
- open_or_save_calendar(): emit one VEVENT per reminder instead of a single
  event with VALARM blocks. Dates come from compute_reminder_offsets(), spread
  across the check-alive period, with the LAST event one day before the
  locktime. Unique UID per event (bal-<wallet>-<offset>d) and numbered
  summaries ' (reminder N/total)'.
- Removed the now-unused create_alarms() method (no more VALARM).
- Default save filename changed from will_event.ics to BAL_will_event.ics.
- common.py: added timedelta to the datetime import.
- plugin_base.py: updated the NUM_REMINDERS comment for the new behaviour.
- tests: replaced the VALARM E1 test with test_e1_build_separate_events_for_giovanna.
- Bumped version to 0.3.7 (4 files) and added CHANGELOG entry #13.

Tests: 239 passed. ruff: no new errors.
2026-06-28 23:01:54 -04:00
donkey-ai
dd2e160a17 fix(bal): black bar on cancelled invalidation + editable fee follows dates (v0.3.6)
Two GUI fixes bundled together (v0.3.6):

- Fix #10 (black bar): in dialogs.py on_success_phase1, the cancelled
  invalidation branch no longer calls the blocking self.wait(3)+self.close().
  wait() uses time.sleep() and runs in the GUI thread, freezing the UI so the
  'Building Will' dialog could not repaint the area it just resized, leaving a
  black rectangle at the bottom. It now shows 'Aborted' plus the existing
  non-blocking Close button (_add_close_button), consistent with the other
  end-of-flow branches.

- Fix #11 (editable fee): in widgets.py WillSettingsWidget.apply_editable_dates,
  the fee widget (baltx_fees) now follows the same 'Editable dates' setting as
  the locktime/threshold widgets (set_read_only(not editable_dates)) instead of
  being forced read-only. With the setting ticked, dates AND fee are editable
  outside the wizard; unticked, all three are read-only. Default stays OFF.

Version bumped 0.3.5 -> 0.3.6 (manifest.json, __init__.py, plugin_base.py,
VERSION). CHANGELOG: entries #10 and #11. ruff clean; full suite 239 passed.
2026-06-28 23:01:38 -04:00
donkey-ai
b03251e864 feat(bal): Group D / D1 - configurable distributed calendar reminders + save-only .ics
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.
2026-06-28 23:00:31 -04:00
donkey-ai
9d2cbc2814 feat(bal): Group C settings-dialog improvements (editable dates, narrower RAW box, warning/reset/support, tooltips, bold locktime)
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.
2026-06-28 23:00:30 -04:00
donkey-ai
dc166d04ff feat(bal): Group A (timestamps + statuses + anticipate docs) and Group B (auto-sign) v0.3.4
Bump plugin version to 0.3.4 (manifest, __init__, plugin_base, VERSION).

GROUP A
- A1: remove block-height locktimes; the plugin now uses UNIX timestamps
  only. The NLOCKTIME_BLOCKHEIGHT_MAX guard is kept on purpose (it forces
  every locktime to be a timestamp). chk_locktime is now 2-arg; int_locktime
  and anticipate_locktime no longer accept blocks; RAW input only accepts d/y.
  Two now-dormant configs (LOCKTIME_BLOCKS, LOCKTIMEDELTA_BLOCKS) are kept with
  comments to avoid touching persisted keys.
- A2: rename PENDING -> MEMPOOL everywhere (label 'Mempool', yellow #ffce30);
  add new UPDATED status; ANTICIPATED & UPDATED keep VALID; documented
  set_status rules; backward-compat migration (old PENDING -> MEMPOOL).
- A3: clarify that anticipating to a future date only rebuilds (never
  invalidates), while only a past locktime invalidates (WillExpired). Code was
  already correct; only the comment and docs were fixed.

Colour follow-up: UPDATED lightened from #800080 to #b266b2 (more readable),
updated in theme.py, docs and the theme test.

GROUP B
- B1: verified the 'Create your will' button already opens the guided wizard
  (no code change needed).
- B2: new persisted AUTO_SIGN setting (default ON) with an 'Auto-sign on Check'
  checkbox in the settings dialog. When enabled, Check signs and broadcasts
  automatically; the wallet password is requested only for encrypted wallets.

B2 follow-up (fixes reported after testing):
- Remove the duplicate sign/broadcast cycle in lists.check(); build_will_task()
  already signs and broadcasts.
- Suppress the manual 'press Sign/Broadcast' hint and its popup when AUTO_SIGN
  is ON (kept when OFF).
- Make broadcast one-shot: removed the retry flag and the Exception('retry');
  failed will-executors stay PUSH_FAIL and are skipped (no endless retry).
  PUSHED transactions are already excluded from re-collection.

Docs: inheritance-options.md/.html and inheritance-flow.svg updated to v0.3.4.
Tests: 206 passing (new test_anticipate_manual_locktime, test_anticipate_past_locktime,
test_group_b_auto_sign; updated core/util, core/will_extra, gui/theme, gui/widgets).
CHANGELOG.md added with one numbered entry per task.
2026-06-28 23:00:23 -04:00
GenSpark AI Developer
30bab62247 UI polish and signed-tx colour fix (v0.3.3)
Fix and refine the BAL plugin GUI without changing business logic:

core/will.py: restore the PUSHED requirement in needs_server_check so a
signed-but-not-broadcast will is no longer server-queried and therefore
stays blue (COMPLETE) instead of turning red (CHECK_FAIL). This matches the
original Gitea check() condition.

gui/qt/widgets.py: WillSettingsWidget vertical layout now caps every row to
the widest date-row width and left-aligns them; the leading icons keep their
original HelpButton width.

gui/qt/lists.py + gui/qt/common.py: the wizard toolbar button now shows a
28x28 icon plus a bold 'Create your will' caption (QSize imported).

gui/qt/dialogs.py (BalBuildWillDialog):
- closing summary row labelled 'All done: Ok' with a blank separator above it;
- 'checking variables' capitalised to 'Checking variables' (redundant trailing
  colon dropped);
- final auto-closing countdown replaced by an explicit right-aligned 'Close'
  button; intermediate technical pauses kept; next-steps popup preserved.

gui/qt/window.py + core/plugin_base.py: guide show_message on build, and
sync_hide_filters() in update_all so hide flags refresh immediately.

tests: test_needs_server_check updated; added offscreen preview helpers.
Version bumped to 0.3.3. 186 tests pass; ruff clean (baseline only).
2026-06-28 22:51:28 -04:00
GenSpark AI Developer
365824767b fix(plugin): missed-update fixes, server re-check, and bold Building Will results (v0.3.2)
Revert the v0.3.1 double-invalidation change (it caused an inheritance-list
regression: stale/invalidated wills lingered and heir/date updates became
incoherent) and add several targeted missed-update fixes plus a UI refinement.

Revert (v0.3.1 -> v0.3.2):
- Remove Will.mark_invalidated_by_tx() and its call in
  loop_broadcast_invalidating. core/will.py and gui/qt/dialogs.py are restored
  to the working v0.3.0 behaviour. The postpone double-invalidation issue is
  intentionally left open, to be addressed without touching the shared
  broadcast path.

FIX 1 - detect heir removal on Check / Electrum close:
- core/will.py (check_willexecutors_and_heirs): the else-branch now raises
  HeirNotFoundException when a will still carries an heir that is no longer in
  the current heirs set (heir removed), mirroring the existing 'heir added'
  path. Rebuild therefore triggers on Check and on_close (same build_will_task
  path), as decided by the user (manual update only, no auto-rebuild).

FIX 2 - Check queries servers for already-sent wills:
- core/will.py: new Will.needs_server_check(w) returns True for any VALID will
  with a will-executor that is not yet CHECKED (no longer limited to PUSHED).
- gui/qt/lists.py (PreviewList.check): use needs_server_check so wills stuck on
  'New / Not sent' are re-checked instead of reporting 'nothing to do'.

FIX 3 - Settings-dialog hide toggles refresh the list:
- core/plugin_base.py: new sync_hide_filters() re-reads the cached
  _hide_invalidated / _hide_replaced flags from the persisted config.
- gui/qt/window.py (update_all): call sync_hide_filters() before refreshing, so
  toggling 'Hide Invalidated' / 'Hide Replaced' in the Settings dialog (which
  writes the config directly) updates the transaction list immediately instead
  of requiring an Electrum restart.

UI - bold results in the Building Will dialog:
- gui/qt/dialogs.py (BalBuildWillDialog): render the right-side results in bold
  (Ok, Ko, Nothing to do, Skipped, Wait, Timeout, ...) keeping the left-side
  state labels in normal weight. Centralised in msg_ok/msg_error/msg_warning/
  msg_set_status, plus the will-executor push/check rows now show Ok/Ko and
  True/False in bold + colour (green/red).

Tests/tooling:
- tests/test_core_will.py: add test_check_heirs_unchanged_is_coherent,
  test_check_heir_removed_triggers_rebuild, test_check_heir_added_triggers_rebuild,
  test_needs_server_check.
- tests/sim_update_flows.py: real-world update-scenario simulation.
- tests/preview_build_will_dialog.py, tests/preview_we_rows.py: GUI-only
  before/after previews of the bold formatting.
- Bump version to 0.3.2 (VERSION, manifest.json, __init__.py, plugin_base.py).

186 tests pass; smoke test, external-zip test and update-flow simulation OK;
ruff reports only pre-existing star-import false positives.
2026-06-28 22:51:28 -04:00
GenSpark AI Developer
e477c5aa5b fix(will): prevent double invalidation when postponing a signed will
When postponing the delivery time of an already signed/sent will, the user
was asked to sign the on-chain invalidation transaction twice before the new
(postponed) will could be built.

Root cause: after the invalidation tx was broadcast, on_success_invalidate
restarted task_phase1 to rebuild the will, but the old will items were still
marked COMPLETE/PUSHED with their original tx.locktime (the on-chain
invalidation did not update the in-memory status). The postpone check therefore
fired WillPostponedException a second time, requesting another invalidation.

Fix:
- Add Will.mark_invalidated_by_tx(will, tx): marks INVALIDATED every valid will
  item that spends a prevout consumed by the just-broadcast invalidation tx.
  Setting INVALIDATED clears the VALID flag, removing those items from
  only_valid_list so the postpone/expire check no longer fires.
- Call it from loop_broadcast_invalidating after a successful broadcast (txid
  obtained) and persist via save_willitems. On the phase-1 restart the old will
  is no longer VALID, so the will is rebuilt directly: a single invalidation
  signature followed by the new will.

Tests: add test_will_mark_invalidated_by_tx and
test_will_mark_invalidated_by_tx_no_match plus the WillPostponedException
hierarchy assertion. 184 tests pass; smoke and external-zip OK; ruff clean.

Bump version to 0.3.1.
2026-06-28 22:51:28 -04:00
GenSpark AI Developer
081e46515f chore(release): bump version to 0.3.0
Bump plugin version from 0.2.8 to 0.3.0 in VERSION, manifest.json,
__init__.py and core/plugin_base.py for the 0.3.0 release (postpone
invalidation safety + Server status column).
2026-06-28 22:51:28 -04:00
GenSpark AI Developer
343046ed34 feat(will): invalidate signed will on postpone + add Server status column
Postpone safety (Strategy B):
- A signed/sent will carries an immutable locktime; postponing the delivery
  time previously did nothing, so a will-executor could still broadcast the
  old (earlier-locktime) transaction and execute the inheritance too early.
- core/will.py: add WillPostponedException and detect postpone by comparing
  the requested locktime against w.tx.locktime (the locktime frozen in the
  signed transaction) instead of the in-memory heir entry, which is updated
  together with the new value and would always compare equal.
- gui/qt/dialogs.py (BalBuildWillDialog.task_phase1, the real path used by
  Tools -> Prepare): handle WillPostponedException before NotCompleteWill;
  return (None, tx) to trigger sign + broadcast of the invalidation, then the
  user presses Prepare again to rebuild/re-sign/re-send (two explicit steps).
- gui/qt/window.py: mirror the branch in build_inheritance_transaction with an
  explanatory message; wording aligned to the 'Prepare' button.
- gui/qt/common.py: export WillPostponedException.
- A postpone on a will that was never signed/sent just rebuilds (no on-chain
  fee).

Server status column:
- gui/qt/lists.py: add a dedicated 'Server' column to PreviewList with an
  always-readable label and a tooltip (will-executor URL + state).
- gui/qt/theme.py: add server_status_text() and server_status_tooltip(),
  reusing the existing status flags.
- gui/qt/common.py: export the new theme helpers.

Docs: update README.md, bal/README.md and CHANGELOG_REFACTOR.md.

Tests: 182 passed; smoke + external-zip OK; ruff has no new real findings.
2026-06-28 22:51:28 -04:00
GenSpark AI Developer
4e027d9e8b fix(qt): auto-close Plugins manager, read-only field styling, RLock-safe heirs persistence
GUI / plugin lifecycle:
- Auto-close Electrum's native 'Electrum Plugins' manager dialog after the
  BAL plugin is hot-enabled. Electrum 4.7.x no longer calls the old init_qt
  hook, so the close is now triggered from the create_status_bar, init_menubar
  and load_wallet hooks (fired when reload_windows() recreates the window).
- Robust dialog matching (isinstance / class name / localized window title) to
  cope with zipimport module-identity mismatches.
- Robust dismissal of the modal dialog (reject()/done()/close()) with a retry
  schedule [400, 800, 1500] ms; if it still cannot be closed, fall back to
  bringing it to the front (showNormal/raise_/activateWindow) so it never
  lingers hidden in the background. Counting only visible top-levels avoids
  treating an already-closed dialog as still open.

Read-only field styling:
- Paint the locked Delivery time / Check Alive date editors and the mining-fee
  spinbox with a light-grey background (#f0f0f0) so the user can see at a glance
  that they are not editable outside the 'Build your will' wizard; the styling
  is cleared when the fields are made editable again.

Pickle/RLock crash on 'Build will':
- heirs.save() now sanitises the heirs mapping via _json_safe() before handing
  it to json_db.put(), which deep-copies the value. A live runtime object
  (holding a threading.RLock) slipping into an heir value previously raised
  'TypeError: cannot pickle _thread.RLock object' and aborted the task; such
  values are now coerced to str and logged with their path.
- init_heirs_to_locktime() coerces the locktime to a plain serializable scalar.
- log_error() now accepts both a sys.exc_info() triple and a single exception
  instance, fixing the secondary 'TypeError object is not subscriptable' that
  masked the real error.
2026-06-28 22:51:28 -04:00
GenSpark AI Developer
13259e881d feat(net): parallel Check + timeout counters + GUI tooltips/order
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.
2026-06-28 22:51:28 -04:00
GenSpark AI Developer
03985a2566 feat(net): invio/ping/download verso Will-Executor in parallelo (anti-freeze)
Problema: i server Will-Executor venivano contattati in sequenza e, su
timeout, send_request riprovava 10x con sleep 3s (~130s per server morto).
Un solo server irraggiungibile bloccava l'intera operazione (impallamento UI).

Soluzione:
- ThreadPoolExecutor: ping e push ora in parallelo (tempo ~= server piu' lento,
  non la somma). Un server morto non blocca piu' gli altri.
- Fast-fail per operazioni interattive (ping/info/download): max_retries=0,
  niente retry-storm.
- Feedback live: callback on_each() aggiorna il dialog server-per-server
  (thread-safe via pyqtSignal di BalWaitingDialog.update).
- Push transazioni: parallelo ma con retry per-server mantenuti (no perdita tx).

File:
- bal/core/willexecutors.py: send_request(+max_retries,+retry_sleep),
  get_info_task fast-fail, NEW ping_servers_parallel(), push_transactions_parallel(),
  DEFAULT_TIMEOUT=5.
- bal/gui/qt/window.py: ping_willexecutors_task + push_transactions_to_willexecutors
  riscritti su helper paralleli con feedback live; fetch_will_executors_list fast-fail.
- bal/core/util.py: BUGFIX get_value_amount usava in_output (bool) invece di
  din_output (tupla) -> TypeError. Scoperto dai test ufficiali Gitea.

Test (contro il codice refactor):
- pytest tests/ ufficiali: 117 core + 65 gui = 182 passed.
- smoke/external_zip/windows_overflow/gui_fixes: OK.
- parallel_ping_test (nuovo): 0.50s per 8 server vs ~4.00s sequenziale.
- ruff: nessun nuovo problema introdotto (codice nuovo PEP8-compliant).

Aggiunti i test ufficiali del repo Gitea + REPORT_NETWORKING_PARALLELO.md.
2026-06-28 22:51:28 -04:00
genspark-ai-developer[bot]
b1c8bba9e9 fix: crash GUI su Windows (OverflowError anno 2038) — schede/menu BAL rotti (#3)
* 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>
2026-06-28 22:51:28 -04:00
GenSpark AI Developer
4198a5145b BAL - Bitcoin After Life Electrum plugin (v0.2.8)
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.
2026-06-07 09:22:34 +00:00