docs: QR/audio will transfer + Android reader companion notes
This commit is contained in:
170
CHANGELOG.md
170
CHANGELOG.md
@@ -3094,4 +3094,174 @@ misleading.
|
|||||||
- Manually tested by the owner in Electrum 4.8.1: `NO_FUTURE_DATE`,
|
- Manually tested by the owner in Electrum 4.8.1: `NO_FUTURE_DATE`,
|
||||||
`WILLEXECUTOR_FEE` and `No Heirs` were all confirmed on screen.
|
`WILLEXECUTOR_FEE` and `No Heirs` were all confirmed on screen.
|
||||||
|
|
||||||
|
## 57. Remove all `copy.deepcopy` (ad-hoc copy helpers; `WillItem` copies serialize/deserialize)
|
||||||
|
|
||||||
|
**Date:** 2026-08-28
|
||||||
|
|
||||||
|
**Goal (owner request):** eliminate every `copy.deepcopy` from the codebase
|
||||||
|
and replace it with ad-hoc copy methods; `WillItem` copies must be produced by
|
||||||
|
serializing and deserializing the item rather than by deep-copying live
|
||||||
|
runtime objects (which can hold a `threading.RLock` and cannot be pickled).
|
||||||
|
|
||||||
|
**What changed:**
|
||||||
|
|
||||||
|
- `bal/core/util.py`: new `copy_structure(value, _path="copy")` — the single
|
||||||
|
JSON-safe, deepcopy-free recursive cloner (dict / list / tuple cloned
|
||||||
|
structurally, JSON scalars kept as-is, any accidental runtime object coerced
|
||||||
|
to `str` + logged). It replaces the old `heirs._json_safe` implementation.
|
||||||
|
- `bal/core/heirs.py`: `_json_safe` is now a thin backward-compatible alias of
|
||||||
|
`bal.core.util.copy_structure`; `Heirs.save` behaviour is unchanged.
|
||||||
|
- `bal/core/will.py`:
|
||||||
|
- `WillItem.__init__` on a `WillItem` argument no longer does
|
||||||
|
`self.__dict__ = w.__dict__.copy()` + `copy.deepcopy`; instead it
|
||||||
|
serializes (`to_dict()`) and deserializes: the tx is re-parsed into a fresh
|
||||||
|
object, `STATUS` is rebuilt from a clone, and heirs / will-executors are
|
||||||
|
cloned recursively, so the copy shares no mutable state with the source.
|
||||||
|
- New `WillItem.copy(wallet=None)` (serialize/deserialize round trip; re-adds
|
||||||
|
wallet tx info when a wallet is passed) and the static
|
||||||
|
`WillItem.copy_status_table(table)` used for the `STATUS` tables.
|
||||||
|
- `to_dict()` now also emits `Father` / `Children` so the round trip is
|
||||||
|
faithful.
|
||||||
|
- `normalize_will` routes copies through the constructor / `copy()`.
|
||||||
|
- `bal/gui/qt/window.py` and `bal/cli/controller.py`: the Build-will flow now
|
||||||
|
uses `copy_structure(...)` instead of `copy.deepcopy(...)` for heirs and
|
||||||
|
will-executors.
|
||||||
|
- Dropped now-unused `import copy` (`will.py`, `controller.py`, `qt/common.py`,
|
||||||
|
`qt/window.py`).
|
||||||
|
- Tests updated to the same helpers: STATUS tables via
|
||||||
|
`WillItem.copy_status_table`, heirs / built dicts via `copy_structure`
|
||||||
|
(`test_core_will.py`, `test_core_will_invalidate.py`,
|
||||||
|
`test_heir_relative_anchor.py`, `test_anticipate_manual_locktime.py`,
|
||||||
|
`test_no_willexecutor_karen7.py`, `test_reproduce_none_type.py`,
|
||||||
|
`test_group_e_mock_karen7.py`, `test_group_e_karen7_invalidate.py`,
|
||||||
|
`sim_update_flows.py`).
|
||||||
|
|
||||||
|
**Verification:**
|
||||||
|
- Full offline batch `tests/test_core_*.py tests/test_gui_*.py`: 377 passed,
|
||||||
|
only the two pre-existing `test_bt_to_date_*` datetime compare failures
|
||||||
|
remain (identical to HEAD — no regression; `test_heir_relative_anchor`
|
||||||
|
isolated-file failure is pre-existing test-pollution at HEAD too).
|
||||||
|
- Ad-hoc semantics check: `copy()`/ctor copy share no mutable state with the
|
||||||
|
source (mutating source heirs/STATUS does not leak into the copy and vice
|
||||||
|
versa), `copy_status_table` returns fresh lists, `normalize_will` runs.
|
||||||
|
- `ruff` on all touched files: no new violations (4 findings, all pre-existing
|
||||||
|
at HEAD).
|
||||||
|
- `tests/smoke_test.py electrum.plugins.bal`: passed.
|
||||||
|
- `python3 build_zip.py`: 45 files, 343591 bytes, sha256 `aa8f8154…`;
|
||||||
|
`tests/external_zip_test.py bal-electrum-plugin.zip`: passed (Plugin class
|
||||||
|
loads via the zipimport shim).
|
||||||
|
|
||||||
**Outcome:** DONE.
|
**Outcome:** DONE.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next. Animated-QR interop (BC-UR v1/v2, BBQR)
|
||||||
|
|
||||||
|
**Date:** 2026-09-08
|
||||||
|
|
||||||
|
**Goal:** Let BAL export/import a will not only as its own BAL QR frame format
|
||||||
|
but also as BC-UR v1 (`ur:bytes`, BC32 + SHA-256), BC-UR v2 (`ur:bytes`, CBOR
|
||||||
|
bytewords-minimal fountain codes) and BBQR (Coinkite `B$…`) animated-QR
|
||||||
|
sequences, so transfers interoperate with Blockchain Commons / Coldcard-style
|
||||||
|
tools and BitKit. Codecs must be stdlib-only and the export must keep BAL QR
|
||||||
|
as the default.
|
||||||
|
|
||||||
|
**What changed:**
|
||||||
|
|
||||||
|
- `bal/core/animated_qr.py` (new): stdlib-only codec module.
|
||||||
|
- BC32 (bech32_bis checksum, XOR `0x3FFFFFFF`) encode/decode matching the
|
||||||
|
BCR-2020-004/005 reference vectors.
|
||||||
|
- bytewords-minimal encode/decode (BCR-2020-012) with CRC-32 rejection;
|
||||||
|
the word list was transcribed verbatim from the reference C++.
|
||||||
|
- BC-UR v2: CBOR part writer/reader, CRC-32, `choose_fragments`
|
||||||
|
(xoshiro256** + alias + ary-threshold sampler) and XOR-based fountain
|
||||||
|
mixing/solving; emits a redundant mixed wave for loss tolerance.
|
||||||
|
- BC-UR v1: multipart with SHA-256 digest and single-part digest-less
|
||||||
|
frames; `1of1` handling.
|
||||||
|
- BBQR: base32 (encoding `2`), hex (uppercase, `H`) and zlib (lowercase,
|
||||||
|
`Z`, automatic compression fallback) frames; out-of-order reconstruction.
|
||||||
|
- One `AnimatedQrSession` + `detect_format` + `parse_for_detection` for
|
||||||
|
auto-detecting the incoming format and keying the GUI debounce.
|
||||||
|
- Safety caps: `_MAX_SESSION_PARTS = 20000`, `_MAX_MESSAGE_BYTES = 32 MB`,
|
||||||
|
zlib-bomb guard, `TransferConflictError`/`SessionLimitError`.
|
||||||
|
- `bal/gui/qt/dialogs.py`:
|
||||||
|
- Export page (`BalQrExportWidget`) gained a **Format** selector
|
||||||
|
(BAL QR default, BC-UR v1, BC-UR v2, BBQR) reusing the QR-size presets,
|
||||||
|
with per-format intro/format-hint text.
|
||||||
|
- Import page (`BalQrImportWidget`) now routes every frame through
|
||||||
|
`parse_for_detection` + `AnimatedQrSession.add_part`, auto-detecting the
|
||||||
|
format and resetting when the transfer's session key changes; the
|
||||||
|
review/sign step resolves the session and decodes parts uniformly.
|
||||||
|
- `qr_import_accept_frame` generalised to
|
||||||
|
`(state, fmt, session_key, frame_total, index, payload, stable_reads=2)`.
|
||||||
|
- `tests/test_core_animated_qr.py` (new, 32 tests): BC32 spec vectors,
|
||||||
|
bytewords round-trip/CRC, C++ reference-frame decode+re-encode parity
|
||||||
|
(single-part 12B, seq_len=2, seq_len=7), fountain solve with missing pure
|
||||||
|
part, out-of-order/duplicate handling, single/multipart UR v1, BBQR
|
||||||
|
Z/2/H round-trips, runt last part, zlib-bomb guard, detection positive/
|
||||||
|
negative.
|
||||||
|
|
||||||
|
**Verification:**
|
||||||
|
|
||||||
|
- `tests/test_core_animated_qr.py`: 32/32 pass.
|
||||||
|
- `tests/test_gui_qr_transfer.py` (now 36 tests) + `test_gui_export_dialogs.py`: pass.
|
||||||
|
- `ruff` clean on `animated_qr.py`, `dialogs.py` and both test files;
|
||||||
|
`pyright` 0 errors on the touched modules.
|
||||||
|
- `tests/smoke_test.py electrum.plugins.bal`, `python3 build_zip.py` and
|
||||||
|
`external_zip_test.py` all pass.
|
||||||
|
- Full regression: 462 passed; only pre-existing failures remain
|
||||||
|
(`test_bt_to_date_*`, will-invalidate fee, unrelated `sign_transactions`
|
||||||
|
stub test).
|
||||||
|
|
||||||
|
**Notes / caveats:**
|
||||||
|
|
||||||
|
- A real bug was found & fixed during this work: `_ur2_part_cost` used
|
||||||
|
`2 * body_len` but `bytewords_minimal_encode` appends a 4-byte CRC, so every
|
||||||
|
UR v2 frame was undercounted by 8 characters and could overflow the QR
|
||||||
|
budget for large transfers.
|
||||||
|
- UR v1 multipart emits the digest-carrying `1of1/<digest>/<frag>` form for a
|
||||||
|
single part (both headered and headerless single parts are accepted on
|
||||||
|
import); this keeps deterministic digest verification.
|
||||||
|
- Imported payloads are UTF-8 text; the codec sessions do not decode raw
|
||||||
|
binary transfer blobs.
|
||||||
|
|
||||||
|
**Outcome:** DONE (uncommitted).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Animated-QR bugfix: QVideoSink signal wiring + will-export JSON crash
|
||||||
|
|
||||||
|
**Date:** 2026-09-08
|
||||||
|
|
||||||
|
**Goal:** Fix two runtime crashes found by manual testing of the QR paths.
|
||||||
|
|
||||||
|
**What changed:**
|
||||||
|
|
||||||
|
- `bal/gui/qt/dialogs.py`:
|
||||||
|
- `_start_scan`/`_stop_scan` used `QVideoSink.videoFrame.connect/.disconnect`,
|
||||||
|
but on PyQt6 `videoFrame` is the frame **getter method**, not a signal —
|
||||||
|
this raised ``AttributeError: 'builtin_function_or_method' object has no
|
||||||
|
attribute 'connect'`` on camera scan. Switched to the `videoFrameChanged`
|
||||||
|
signal (same wiring Electrum's `QrReaderVideoSurface` uses).
|
||||||
|
- `_stop_scan` now tolerates `AttributeError` when disconnecting the sink
|
||||||
|
and guards the `errorOccurred` disconnect too, so a mid-init failure can
|
||||||
|
never cascade into a second uncaught exception.
|
||||||
|
- `_whole_will_json` (whole-will QR export) serialized ``WillItem.to_dict()``
|
||||||
|
with plain `json.dumps`, crashing with ``TypeError: Object of type
|
||||||
|
Transaction is not JSON serializable`` (the ``tx`` field holds a real
|
||||||
|
``Transaction``). Now uses Electrum's `MyEncoder`, matching `write_json_file`.
|
||||||
|
- `tests/test_gui_qr_transfer.py`: new `test_import_start_stop_scan_signal_wiring`
|
||||||
|
drives the real `QVideoSink` life-cycle with a mocked camera and fails if
|
||||||
|
the signal name regresses to `videoFrame`.
|
||||||
|
- `tests/test_gui_export_dialogs.py`: new
|
||||||
|
`test_qr_whole_will_json_serializes_transaction` covers the JSON export.
|
||||||
|
|
||||||
|
**Verification:**
|
||||||
|
|
||||||
|
- `pytest tests/test_gui_qr_transfer.py tests/test_gui_export_dialogs.py -q`: pass.
|
||||||
|
- Full offline batch `tests/test_core_*.py tests/test_gui_*.py`: 444 passed.
|
||||||
|
- Regression test flips correctly (fails when reverted to the buggy call).
|
||||||
|
- `ruff` clean on touched files; `tests/smoke_test.py`, `build_zip.py`,
|
||||||
|
`external_zip_test.py` all pass.
|
||||||
|
|
||||||
|
**Outcome:** DONE (uncommitted).
|
||||||
|
|||||||
@@ -23,6 +23,25 @@ is updated to mark them as supported.
|
|||||||
See [`README.md`](README.md) for supported Electrum versions (currently 4.7.2
|
See [`README.md`](README.md) for supported Electrum versions (currently 4.7.2
|
||||||
and 4.8.0).
|
and 4.8.0).
|
||||||
|
|
||||||
|
## QR wire-format compatibility
|
||||||
|
|
||||||
|
BAL exports/imports wills as QR codes. **BAL QR** (the default) is the plugin's
|
||||||
|
own frame format and is only understood by BAL itself. The export page also
|
||||||
|
supports **BC-UR v1**, **BC-UR v2** and **BBQR**:
|
||||||
|
|
||||||
|
| Format | Wire appearance | Interop target |
|
||||||
|
|-----------|----------------------------|------------------------------------------------------|
|
||||||
|
| BAL QR | `BALQR1\|total\|index\|…` | Past/other BAL versions (default, always exported) |
|
||||||
|
| BC-UR v1 | `ur:bytes/<bc32>` | Blockchain Commons / Coldcard-style UR (BC32, SHA-256 digest, part counts per part) |
|
||||||
|
| BC-UR v2 | `ur:bytes/<seq>-<seqlen>/<bytewords>` | BC-UR 2.x fountain codes (CBOR parts, CRC-32, bytewords-minimal) |
|
||||||
|
| BBQR | `B$<enc><type><N><n>…` | Coinkite BitKit / Coldcard's BBQR animated-QR mode |
|
||||||
|
|
||||||
|
Import auto-detects the format of each scanned code; out-of-order, duplicate
|
||||||
|
and (for UR v2) partially-lost fountain frames are handled. Interop is
|
||||||
|
validation-tested against the reference C++ bc-ur encoder output and the
|
||||||
|
BCR-2020-004/005 BC32 test vectors; it has not yet been cross-verified against
|
||||||
|
third-party libraries (`ur`, `bbqr`, Coldcard firmwares).
|
||||||
|
|
||||||
## Reporting compatibility issues
|
## Reporting compatibility issues
|
||||||
|
|
||||||
If you find a compatibility problem not listed here, please open an issue on
|
If you find a compatibility problem not listed here, please open an issue on
|
||||||
|
|||||||
81
HANDOFF.md
81
HANDOFF.md
@@ -395,19 +395,74 @@ See Section 5 for details.
|
|||||||
|
|
||||||
### In progress: QR / audio will transfer (branch `feature/bal-qr-transfer`)
|
### In progress: QR / audio will transfer (branch `feature/bal-qr-transfer`)
|
||||||
|
|
||||||
- P0–P4 of `PLAN_QR_TRANSFER.md` are implemented on
|
- The full QR-transfer feature (P0–P6) is implemented, tested and committed on
|
||||||
`feature/bal-qr-transfer` and un-committed: core scheduler
|
`feature/bal-qr-transfer` (commits `d288b55`, `ce3e36d`, pushed to
|
||||||
(`bal/core/qrtransfer.py`), settings row, export/import dialogs +
|
`origin`). PR creation URL:
|
||||||
review/sign wizard + lists/window wiring, all with tests
|
`https://bitcoin-after.life/gitea/bitcoinafterlife/bal-electrum-plugin/pulls/new/feature/bal-qr-transfer`
|
||||||
(`tests/test_core_qr_transfer.py`, `tests/test_gui_qr_transfer.py`) and
|
- Included: core scheduler (`bal/core/qrtransfer.py`), `QR_CHUNK_SIZE`
|
||||||
docs (README, CHANGELOG entry 56, QML_PLAN, this file).
|
setting (4 export presets), export/import dialogs + review/sign wizard +
|
||||||
- Remaining: **P6** release hygiene — `python3 build_zip.py`, external-zip
|
lists/window wiring, export filters, auto slideshow with per-second rate +
|
||||||
test, full ruff check (no NEW violations; baseline is intentionally dirty),
|
loop option, audio send/receive buttons, and the crash fixes
|
||||||
and a manual on-device walkthrough of the QR path. The audio path is
|
(`status` default, `invalidate_will` guard). Docs: README, CHANGELOG entry
|
||||||
optional and hidden until the `audio_modem` plugin + `amodem` are
|
56, QML_PLAN, `AUDIO_MODEM_DEBIAN.md`.
|
||||||
installed.
|
- Follow-up refactor (CHANGELOG entry 57): all `copy.deepcopy` removed —
|
||||||
- Commit policy unchanged: nothing is committed until the owner explicitly
|
`copy_structure()` in `bal/core/util.py`, `WillItem.copy()` / ctor
|
||||||
confirms.
|
serialize/deserialize, `copy_status_table()`. Working tree clean after the
|
||||||
|
branch's three commits.
|
||||||
|
- Verification: batch 377 passed / 2 pre-existing `test_bt_to_date_*`
|
||||||
|
failures; ruff no new violations; smoke + `build_zip.py` +
|
||||||
|
external-zip OK; pyright clean. The isolated
|
||||||
|
`test_heir_relative_anchor.py::test_karen7_frozen_delivery_not_expired`
|
||||||
|
failure is pre-existing test pollution (fails identically on clean HEAD,
|
||||||
|
passes inside the full batch) — not caused by entry 57.
|
||||||
|
- Remaining: manual on-device walkthrough of the QR path (and, if wanted,
|
||||||
|
the audio path — buttons only appear when the `audio_modem` plugin +
|
||||||
|
`amodem` are installed; see the prerequisites below).
|
||||||
|
|
||||||
|
### In progress: animated-QR interop (BC-UR v1/v2, BBQR)
|
||||||
|
|
||||||
|
- `bal/core/animated_qr.py` implements stdlib-only codecs for **BC-UR v1**
|
||||||
|
(BC32 + SHA-256 digest; the bech32_bis checksum variant per
|
||||||
|
BCR-2020-004/005), **BC-UR v2** (CBOR part structure, bytewords-minimal,
|
||||||
|
CRC-32, xoshiro256-based fountain with alias-sampled mixing) and **BBQR**
|
||||||
|
(Coinkite `B$…` base32/hex/zlib frames), plus one shared
|
||||||
|
`AnimatedQrSession` with `detect_format` auto-detection and
|
||||||
|
`parse_for_detection` frame identity for the GUI debounce.
|
||||||
|
- Current status as of this session: reference parity, GUI, and tests done;
|
||||||
|
not yet committed.
|
||||||
|
- **BC32/bytewords/codec parity:** BC32 reproduces the BCR-2020-004/005
|
||||||
|
test vectors (`Hello, world`, `Hello world`, the long seed vector);
|
||||||
|
bytewords-minimal round-trips with CRC rejection; UR v2 part encode +
|
||||||
|
decode is byte-exact against the reference C++ bc-ur encoder for a
|
||||||
|
single part, seq_len=2 (12 frames) and seq_len=7 (3 sampled mixes),
|
||||||
|
validating CBOR framing, bytewords, alias+ary-threshold sampling,
|
||||||
|
xoshiro256** and the XOR mix.
|
||||||
|
- **Sessions:** UR v2 single-part (no seq header), out-of-order frames,
|
||||||
|
duplicate drops, solve with a missing pure fragment (a second redundant
|
||||||
|
mixed wave is emitted by `ur2_frames`), UR v1 single-part
|
||||||
|
(digest-less `ur:bytes/<bc32>` accepted) and multipart, BBQR full-frame
|
||||||
|
decode in any order for Z/2/H encodings.
|
||||||
|
- **Safety:** `_MAX_SESSION_PARTS = 20000`, `_MAX_MESSAGE_BYTES = 32 MB`,
|
||||||
|
`TransferConflictError` on a frame from a different transfer,
|
||||||
|
`SessionLimitError`, BBQR zlib-bomb guard, UTF-8 payloads only.
|
||||||
|
- **GUI:** `BalQrExportWidget` gained a Format selector (BAL QR default,
|
||||||
|
BC-UR v1, BC-UR v2, BBQR) reusing the QR-size presets; the importer now
|
||||||
|
routes every frame through `detect_format` +
|
||||||
|
`AnimatedQrSession.add_part` with the shared
|
||||||
|
`qr_import_accept_frame(state, fmt, session_key, frame_total, index,
|
||||||
|
payload, stable_reads=2)` debounce (reset on session-key change).
|
||||||
|
`_review_and_sign` resolves the session to the transfer text and decodes
|
||||||
|
parts uniformly across formats.
|
||||||
|
- **Verification:** `tests/test_core_animated_qr.py` (32 tests incl. the
|
||||||
|
C++-reference parity vectors and BC32 spec vectors) and the extended
|
||||||
|
`tests/test_gui_qr_transfer.py` pass; ruff clean on the new/changed
|
||||||
|
files; pyright 0 errors; smoke test, `build_zip.py` and
|
||||||
|
`external_zip_test.py` green. Only the pre-existing failures remain
|
||||||
|
(`test_bt_to_date_*`, fee-exceeds-balance, karen7 pollution).
|
||||||
|
- **Any remaining work:** manual on-device walkthrough of the QR path with
|
||||||
|
the new formats; optionally validate against third-party libraries
|
||||||
|
(`ur`, `bbqr`) once available; add the docstrings/branch notes already
|
||||||
|
captured in `ag1.md`/`ag2.md` context where needed.
|
||||||
|
|
||||||
**Dev-box audio prerequisites (audio_modem channel):**
|
**Dev-box audio prerequisites (audio_modem channel):**
|
||||||
See `AUDIO_MODEM_DEBIAN.md` — the full Debian setup + verification, with the
|
See `AUDIO_MODEM_DEBIAN.md` — the full Debian setup + verification, with the
|
||||||
|
|||||||
26
README.md
26
README.md
@@ -26,7 +26,8 @@ bal/ the installable Electrum plugin package
|
|||||||
│ ├── willexecutors.py
|
│ ├── willexecutors.py
|
||||||
│ ├── checkalive.py
|
│ ├── checkalive.py
|
||||||
│ ├── reminders.py
|
│ ├── reminders.py
|
||||||
│ ├── qrtransfer.py QR will-transfer wire format / chunk scheduler
|
│ ├── qrtransfer.py BAL QR will-transfer wire format / chunk scheduler
|
||||||
|
│ ├── animated_qr.py BC-UR v1/v2 + BBQR codecs (stdlib-only)
|
||||||
│ └── input_rules.py
|
│ └── input_rules.py
|
||||||
├── cli/ headless command-line layer (no Qt)
|
├── cli/ headless command-line layer (no Qt)
|
||||||
│ ├── commands.py bal_* daemon commands (@plugin_command)
|
│ ├── commands.py bal_* daemon commands (@plugin_command)
|
||||||
@@ -97,7 +98,19 @@ export offers All / Valid / Valid-NC filters plus a QR size preset
|
|||||||
one at a time, then proposes exporting the signed transactions. When
|
one at a time, then proposes exporting the signed transactions. When
|
||||||
Electrum's `audio_modem` plugin is enabled (optional, requires `amodem` +
|
Electrum's `audio_modem` plugin is enabled (optional, requires `amodem` +
|
||||||
PortAudio) Send/Receive audio buttons complement the QR channel. See
|
PortAudio) Send/Receive audio buttons complement the QR channel. See
|
||||||
[`PLAN_QR_TRANSFER.md`](PLAN_QR_TRANSFER.md) for the full wire-format spec.
|
[`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
|
## Command-line / headless usage
|
||||||
|
|
||||||
@@ -195,6 +208,15 @@ QT_QPA_PLATFORM=offscreen python3 tests/smoke_test.py electrum.plugins.bal
|
|||||||
QT_QPA_PLATFORM=offscreen python3 tests/external_zip_test.py bal-electrum-plugin.zip
|
QT_QPA_PLATFORM=offscreen python3 tests/external_zip_test.py bal-electrum-plugin.zip
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Companion: Android reader
|
||||||
|
|
||||||
|
The `android/` subfolder contains **BAL Reader**, a simple Android app that
|
||||||
|
reads the QR wills this plugin exports (BAL QR, BC-UR v1/v2, BBQR) straight
|
||||||
|
from the screen with your phone — view, copy, share, or save the recovered
|
||||||
|
will. It reuses the plugin's own QR codecs via Chaquopy (the very Python
|
||||||
|
modules that power the desktop import), so decoding behaviour is identical.
|
||||||
|
See [`android/README.md`](android/README.md).
|
||||||
|
|
||||||
## ⚠️ Safety
|
## ⚠️ Safety
|
||||||
|
|
||||||
This plugin builds real Bitcoin inheritance transactions with time-locks. Test
|
This plugin builds real Bitcoin inheritance transactions with time-locks. Test
|
||||||
|
|||||||
Reference in New Issue
Block a user