222 lines
9.8 KiB
Markdown
222 lines
9.8 KiB
Markdown
# BAL — Bitcoin After Life (Electrum plugin)
|
||
|
||
Free and decentralized **Bitcoin inheritance** support for the
|
||
[Electrum](https://electrum.org) wallet. Build time-locked "will" transactions
|
||
that transfer your funds to your heirs if you stop refreshing them
|
||
(dead-man's switch), optionally relayed by will-executor servers.
|
||
|
||
This repository contains a **refactored and extended** version of the original
|
||
plugin. The logic was reorganized to cleanly separate **business logic** from the
|
||
**PyQt GUI**, and new features have been added including a headless CLI,
|
||
auto-rebuild on new transactions, OP_RETURN heirs, and configurable calendar
|
||
reminders.
|
||
|
||
## Repository layout
|
||
|
||
```
|
||
bal/ the installable Electrum plugin package
|
||
├── manifest.json plugin metadata (Electrum reads this)
|
||
├── qt.py Qt entry-point shim (re-exports Plugin)
|
||
├── cmdline.py CLI entry-point shim (re-exports Plugin)
|
||
├── core/ GUI-free logic (importable without Qt)
|
||
│ ├── util.py
|
||
│ ├── plugin_base.py
|
||
│ ├── heirs.py
|
||
│ ├── will.py
|
||
│ ├── willexecutors.py
|
||
│ ├── checkalive.py
|
||
│ ├── reminders.py
|
||
│ ├── qrtransfer.py BAL QR will-transfer wire format / chunk scheduler
|
||
│ ├── animated_qr.py BC-UR v1/v2 + BBQR codecs (stdlib-only)
|
||
│ └── input_rules.py
|
||
├── cli/ headless command-line layer (no Qt)
|
||
│ ├── commands.py bal_* daemon commands (@plugin_command)
|
||
│ ├── controller.py headless BalController (replicates BalWindow)
|
||
│ └── plugin.py CLI Plugin entry point
|
||
├── gui/qt/ PyQt6 presentation layer
|
||
│ ├── theme.py status → color mapping
|
||
│ ├── common.py shared imports / helpers
|
||
│ ├── widgets.py leaf widgets
|
||
│ ├── calendar.py calendar widget
|
||
│ ├── dialogs.py dialog windows
|
||
│ ├── lists.py tree/list views
|
||
│ ├── window.py per-wallet GUI controller
|
||
│ ├── window_utils.py GUI utility helpers
|
||
│ └── plugin.py Plugin (Electrum @hooks → GUI)
|
||
├── icons/ wallet_util/ LICENSE README.md
|
||
build_zip.py builds a clean, zipimport-friendly distribution zip
|
||
tests/ smoke + external-zip regression tests
|
||
```
|
||
|
||
## Requirements
|
||
|
||
- **Electrum 4.7.2 or 4.8.0** — the plugin detects which wallet-DB
|
||
registration API is available (`json_db.register_dict` on 4.7.2,
|
||
`stored_dict.register_name` on 4.8.0) and adapts automatically.
|
||
- **PyQt6** (bundled with the Electrum desktop GUI).
|
||
|
||
## Wallet compatibility
|
||
|
||
BAL currently supports **standard (single-signature) wallets** and
|
||
**hardware wallets** supported by Electrum. **Multisig wallets** and
|
||
**Electrum TrustedCoin (2FA) wallets** are **not yet supported** — see
|
||
[`COMPATIBILITY.md`](COMPATIBILITY.md) for the full compatibility matrix and
|
||
current status.
|
||
|
||
## Installation
|
||
|
||
### Build the distribution archive
|
||
|
||
```bash
|
||
python3 build_zip.py
|
||
# -> bal-electrum-plugin.zip (prints size + SHA-256 for integrity checks)
|
||
```
|
||
|
||
The builder writes a `zipimport`-friendly archive (files only, standard
|
||
DEFLATE, deterministic order) to avoid loader errors seen on some Electrum
|
||
portable builds.
|
||
|
||
### Install as an external plugin (zip)
|
||
|
||
1. Electrum → **Tools → Plugins** → install from file → pick the built zip.
|
||
2. Enable **Bitcoin After Life** and restart Electrum.
|
||
3. (Recommended) verify the downloaded zip's SHA-256 matches the value printed
|
||
by `build_zip.py`.
|
||
|
||
### Install as an internal plugin
|
||
|
||
Copy the `bal/` directory into your Electrum installation's
|
||
`electrum/plugins/` directory, so that `electrum/plugins/bal/manifest.json`
|
||
exists, then enable it from **Tools → Plugins**.
|
||
|
||
## Transfer a will with QR codes (or audio)
|
||
|
||
From the will list (**Export → QR Codes**) a will can be exported as a
|
||
sequence of QR codes and imported on another device (**Import via QR**). The
|
||
export offers All / Valid / Valid-NC filters plus a QR size preset
|
||
(150–1800 bytes/frame) and ships the default **BAL QR** format already
|
||
compressed whenever that is smaller (best-of zlib, flag per frame); the import
|
||
flow reviews and sign each transaction
|
||
one at a time, then proposes exporting the signed transactions. When
|
||
Electrum's `audio_modem` plugin is enabled (optional, requires `amodem` +
|
||
PortAudio) Send/Receive audio buttons complement the QR channel. See
|
||
[`PLAN_QR_TRANSFER.md`](PLAN_QR_TRANSFER.md) for the BAL QR wire-format spec.
|
||
|
||
### Animated-QR formats (interop)
|
||
|
||
BAL QR is the default export format, but the export page's **Format** selector
|
||
also emits **BC-UR v1** (`ur:bytes`, BC32 + SHA-256), **BC-UR v2**
|
||
(`ur:bytes`, CBOR fountain codes) and **BBQR** (`B$…`, Coinkite, used by
|
||
BitKit) animated-QR sequences. The importer auto-detects the format of each
|
||
code it sees, so any of the four formats can be imported on a BAL device, and
|
||
a BAL export can be imported by any tool that understands these standards.
|
||
UR v2 imports tolerate out-of-order and duplicate frames (fountain decoding);
|
||
BBQR frames may arrive in any order. Rotation/redundancy caps and the
|
||
32 MB message limit (zlib-bomb guard) bound untrusted scanner input.
|
||
|
||
## Command-line / headless usage
|
||
|
||
BAL can be used without the Qt GUI via Electrum's daemon mode. The CLI layer
|
||
exposes `bal_*` commands that replicate the full inheritance cycle.
|
||
|
||
### Prerequisites
|
||
|
||
- An **Electrum daemon** running (`electrum daemon -d`)
|
||
- A wallet loaded (`electrum load_wallet`)
|
||
|
||
### Available commands
|
||
|
||
| Category | Commands |
|
||
|----------|----------|
|
||
| Settings | `bal_settings_list`, `bal_settings_get`, `bal_settings_set`, `bal_settings_reset` |
|
||
| Heirs | `bal_heirs_list`, `bal_heirs_show`, `bal_heirs_add`, `bal_heirs_update`, `bal_heirs_delete`, `bal_heirs_import`, `bal_heirs_export` |
|
||
| Will-Executors | `bal_willexecutors_list`, `bal_willexecutors_show`, `bal_willexecutors_add`, `bal_willexecutors_update`, `bal_willexecutors_select`, `bal_willexecutors_delete`, `bal_willexecutors_ping`, `bal_willexecutors_download`, `bal_willexecutors_import`, `bal_willexecutors_export` |
|
||
| Will | `bal_will_status`, `bal_will_check`, `bal_will_prepare`, `bal_will_autorebuild`, `bal_will_sign`, `bal_will_broadcast`, `bal_will_export`, `bal_will_import_merge`, `bal_will_invalidate`, `bal_will_check_executor` |
|
||
|
||
### Example workflow
|
||
|
||
```bash
|
||
electrum daemon -d
|
||
electrum load_wallet
|
||
electrum bal_heirs_list
|
||
electrum bal_will_prepare
|
||
electrum bal_will_sign --password '...'
|
||
electrum bal_will_broadcast
|
||
electrum stop
|
||
```
|
||
|
||
All commands require a running daemon (Electrum's `plugin_command` enforces
|
||
this). Wallet-bound commands (`bal_heirs_*`, `bal_will_*`, etc.) require the
|
||
wallet to be loaded first. Signing commands require `--password` for encrypted
|
||
wallets.
|
||
|
||
## Inheritance safety: anticipate / postpone
|
||
|
||
A will transaction is signed with a **fixed, immutable locktime** and then
|
||
optionally sent to will-executor servers, which are economically incentivised
|
||
to broadcast it (they collect fees). Because the locktime is baked into the
|
||
signed transaction, simply changing the delivery time later is **not enough**:
|
||
the old, already-signed transaction keeps living on the will-executors.
|
||
|
||
The plugin handles the cases as follows (triggered when you press
|
||
**Prepare** on the **WILL** tab):
|
||
|
||
* **Anticipate** (new delivery time *earlier* than the signed locktime, still
|
||
in the future): a plain **rebuild** — the transactions are re-created with
|
||
the new, earlier locktime. **No on-chain invalidation and no Bitcoin fee**,
|
||
even if the will was already signed/sent: moving the date earlier only makes
|
||
the inheritance available *sooner*, so there is no early-execution risk.
|
||
* **Expire** (new delivery time now in the **past**): the will is genuinely
|
||
expired and you are asked to **invalidate** the old transaction on-chain,
|
||
then rebuild.
|
||
* **Postpone** (new delivery time *later* than the signed locktime) on a will
|
||
that was already **signed and/or pushed**: the previously committed coins
|
||
must be invalidated on-chain **first**, otherwise a will-executor could
|
||
broadcast the old (earlier-locktime) transaction and execute the inheritance
|
||
*too early*. The plugin detects this by comparing the requested locktime with
|
||
the locktime **frozen inside the signed transaction** (`tx.locktime`), and
|
||
asks you to sign and broadcast an invalidation transaction. After it is
|
||
broadcast, press **Prepare** again to rebuild, re-sign and re-send the new
|
||
(postponed) inheritance. Postponing a will that was *never* signed/sent just
|
||
rebuilds it (no on-chain fee).
|
||
|
||
## Transaction list: the "Server" column
|
||
|
||
The will transaction list shows a dedicated **Server** column so you always
|
||
know whether each inheritance transaction is actually stored on the
|
||
will-executor servers, independently of the row colour:
|
||
|
||
| Label | Meaning |
|
||
| --- | --- |
|
||
| `Confirmed on server` | the will-executor confirmed it stored the transaction |
|
||
| `Sent (not checked)` | pushed to the will-executor, not yet re-checked |
|
||
| `Send failed` / `Not on server` | push failed or the server no longer has it |
|
||
| `Signed (not sent)` | signed locally, not sent to any will-executor |
|
||
| `Not sent` | not signed/sent yet |
|
||
|
||
Hovering the cell shows a tooltip with the will-executor URL and the current
|
||
state.
|
||
|
||
## Testing
|
||
|
||
Run the tests with the **runtime environment** active (see `HANDOFF.md` §3 for
|
||
the two venvs and how to activate them):
|
||
|
||
```bash
|
||
# imports + behavior
|
||
QT_QPA_PLATFORM=offscreen python3 tests/smoke_test.py electrum.plugins.bal
|
||
|
||
# external-zip loading regression (run after build_zip.py)
|
||
QT_QPA_PLATFORM=offscreen python3 tests/external_zip_test.py bal-electrum-plugin.zip
|
||
```
|
||
|
||
## ⚠️ Safety
|
||
|
||
This plugin builds real Bitcoin inheritance transactions with time-locks. Test
|
||
on **testnet** or a fund-less wallet first, and review the generated
|
||
transactions before broadcasting.
|
||
|
||
## License
|
||
|
||
MIT — see [`bal/LICENSE`](bal/LICENSE).
|