forked from bitcoinafterlife/bal-electrum-plugin
add bal root files
This commit is contained in:
21
bal/LICENSE
Normal file
21
bal/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 copronista
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
23
bal/README.md
Normal file
23
bal/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# BalPlugin
|
||||
Bitcoin After Life Electrum Plugin
|
||||
|
||||
Free and decentralized Bitcoin inheritance support for Electrum: 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.
|
||||
|
||||
## Key behaviours
|
||||
|
||||
- **Anticipate / postpone safety**: changing the delivery time of an
|
||||
already-signed will is handled safely. Postponing a signed/sent will first
|
||||
asks you to invalidate the old transaction on-chain (so a will-executor can
|
||||
never broadcast the earlier-locktime transaction and execute the inheritance
|
||||
too early), then lets you rebuild and re-send the new one via
|
||||
**Tools → Prepare**.
|
||||
- **"Server" column**: the will transaction list shows whether each transaction
|
||||
is actually stored on the will-executor servers
|
||||
(`Confirmed on server`, `Sent (not checked)`, `Send failed`,
|
||||
`Not on server`, `Signed (not sent)`, `Not sent`), with a tooltip showing the
|
||||
will-executor URL.
|
||||
|
||||
See the top-level [`README.md`](../README.md) for installation and testing.
|
||||
1
bal/VERSION
Normal file
1
bal/VERSION
Normal file
@@ -0,0 +1 @@
|
||||
0.3.3
|
||||
37
bal/__init__.py
Normal file
37
bal/__init__.py
Normal file
@@ -0,0 +1,37 @@
|
||||
"""BAL - Bitcoin After Life Electrum plugin.
|
||||
|
||||
Free and decentralized Bitcoin inheritance support for the Electrum wallet.
|
||||
|
||||
This package was reorganized (Approach A: conservative, behavior-preserving)
|
||||
to cleanly separate logic from presentation. The original monolithic plugin
|
||||
mixed the business logic with the PyQt GUI; here the two concerns live in
|
||||
distinct sub-packages:
|
||||
|
||||
bal/
|
||||
core/ GUI-free business logic (importable without Qt)
|
||||
util.py Generic helpers (encoding, validation, ...)
|
||||
plugin_base.py BasePlugin subclass, config, timestamp handling
|
||||
heirs.py Heir list model + transaction building
|
||||
will.py Will / WillItem domain model
|
||||
willexecutors.py Will-executor (dead-man's switch) networking
|
||||
gui/
|
||||
qt/ PyQt6 presentation layer
|
||||
theme.py Colors / status -> color mapping (status_color)
|
||||
common.py Shared imports and small GUI helpers
|
||||
widgets.py Leaf widgets (editors, labels, checkboxes, ...)
|
||||
calendar.py BalCalendar widget
|
||||
dialogs.py Dialog windows (wizard, build-will, detail, ...)
|
||||
lists.py Tree/list views (heirs, preview, will-executors)
|
||||
window.py BalWindow controller (per-wallet GUI state)
|
||||
plugin.py Plugin class wiring Electrum @hooks to the GUI
|
||||
qt.py Thin loader shim re-exporting `Plugin` for Electrum
|
||||
|
||||
Electrum discovers the plugin through ``manifest.json`` and loads the GUI
|
||||
entry point from ``qt.py`` (the shim), which imports the real ``Plugin``
|
||||
from ``gui.qt.plugin``.
|
||||
|
||||
The plugin targets Electrum 4.7.2 (the last stable release exposing
|
||||
``json_db.register_dict``) and PyQt6.
|
||||
"""
|
||||
|
||||
__version__ = "0.3.3"
|
||||
14
bal/bal_resources.py
Normal file
14
bal/bal_resources.py
Normal file
@@ -0,0 +1,14 @@
|
||||
import os
|
||||
|
||||
PLUGIN_DIR = os.path.split(os.path.realpath(__file__))[0]
|
||||
DEFAULT_ICON = "bal32x32.png"
|
||||
DEFAULT_ICON_PATH = "icons"
|
||||
|
||||
|
||||
def icon_path(icon_basename: str = DEFAULT_ICON):
|
||||
path = resource_path(DEFAULT_ICON_PATH, icon_basename)
|
||||
return path
|
||||
|
||||
|
||||
def resource_path(*parts):
|
||||
return os.path.join(PLUGIN_DIR, *parts)
|
||||
10
bal/manifest.json
Normal file
10
bal/manifest.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "bal",
|
||||
"fullname": "Bitcoin After Life",
|
||||
"version": "0.3.3",
|
||||
"description": "Provides free and decentralized Bitcoin inheritance support. Build time-locked 'will' transactions that transfer funds to your heirs if you stop refreshing them (dead-man's switch), optionally relayed by will-executor servers.",
|
||||
"author": "Svatantrya",
|
||||
"licence": "MIT",
|
||||
"available_for": ["qt"],
|
||||
"icon": "icons/bal32x32.png"
|
||||
}
|
||||
83
bal/qt.py
Normal file
83
bal/qt.py
Normal file
@@ -0,0 +1,83 @@
|
||||
"""
|
||||
bal.qt
|
||||
======
|
||||
|
||||
Compatibility shim for Electrum's plugin loader.
|
||||
|
||||
Electrum loads a Qt plugin by importing the ``qt`` module of the plugin package
|
||||
and looking for a ``Plugin`` class. The real implementation lives in the
|
||||
well-separated ``bal.gui.qt`` sub-package, so this module re-exports the
|
||||
``Plugin`` class from ``bal.gui.qt.plugin``.
|
||||
|
||||
Why this file is not a one-line relative import
|
||||
-----------------------------------------------
|
||||
A plain ``from .gui.qt.plugin import Plugin`` works fine when the plugin is
|
||||
installed as an *internal* plugin (under ``electrum/plugins/bal``). However,
|
||||
when the very same code is loaded as an *external* plugin from a ``.zip``,
|
||||
Electrum 4.7.x imports the package under the synthetic top-level name
|
||||
``electrum_external_plugins.bal`` and only executes the package ``__init__`` and
|
||||
this ``qt`` module. It never registers the intermediate parent packages
|
||||
(``electrum_external_plugins`` itself, ``...bal.gui``, ``...bal.gui.qt``). As a
|
||||
result, a relative import that has to walk up to those parents fails with::
|
||||
|
||||
ModuleNotFoundError: No module named 'electrum_external_plugins'
|
||||
|
||||
To make the plugin work *both* as an internal package and as an external zip,
|
||||
this shim resolves and imports ``Plugin`` defensively:
|
||||
|
||||
1. It works out the name of the package this module lives in
|
||||
(``__package__``), whatever Electrum decided to call it.
|
||||
2. It makes sure every parent package in that chain exists in
|
||||
``sys.modules`` so Python's import machinery can resolve sub-modules.
|
||||
3. It imports the ``.gui.qt.plugin`` sub-module via :func:`importlib.import_module`
|
||||
using the resolved absolute name.
|
||||
|
||||
This keeps the clean ``core`` / ``gui`` layout while staying robust to how the
|
||||
plugin is loaded.
|
||||
"""
|
||||
|
||||
import importlib
|
||||
import sys
|
||||
|
||||
|
||||
def _ensure_parent_packages(pkg_name: str) -> None:
|
||||
"""Make sure every ancestor package of *pkg_name* is in ``sys.modules``.
|
||||
|
||||
When loaded from a zip as an external plugin, Electrum only executes the
|
||||
plugin package ``__init__`` and the ``qt`` module. The synthetic root
|
||||
package (e.g. ``electrum_external_plugins``) and any intermediate packages
|
||||
may be missing from ``sys.modules``, which breaks relative/absolute
|
||||
sub-module imports. We backfill them here using this module's own loader
|
||||
so that ``importlib`` can find sibling sub-packages.
|
||||
"""
|
||||
parts = pkg_name.split(".")
|
||||
# Walk from the top-most ancestor down to (but not including) pkg_name.
|
||||
for i in range(1, len(parts)):
|
||||
ancestor = ".".join(parts[:i])
|
||||
if ancestor in sys.modules:
|
||||
continue
|
||||
try:
|
||||
importlib.import_module(ancestor)
|
||||
except Exception:
|
||||
# The synthetic root (e.g. 'electrum_external_plugins') often has no
|
||||
# real spec. Create a minimal namespace package stub so that the
|
||||
# import machinery can still resolve its children.
|
||||
import types
|
||||
|
||||
module = types.ModuleType(ancestor)
|
||||
module.__path__ = [] # mark as a (namespace) package
|
||||
sys.modules[ancestor] = module
|
||||
|
||||
|
||||
# The package this module belongs to. Could be 'electrum.plugins.bal' (internal)
|
||||
# or 'electrum_external_plugins.bal' (external zip), depending on how Electrum
|
||||
# loaded us.
|
||||
_PKG = __package__ or "bal"
|
||||
|
||||
_ensure_parent_packages(_PKG)
|
||||
|
||||
# Import the real implementation using the fully-qualified, run-time package
|
||||
# name so it works regardless of the synthetic prefix Electrum assigned.
|
||||
_plugin_module = importlib.import_module(_PKG + ".gui.qt.plugin")
|
||||
|
||||
Plugin = _plugin_module.Plugin # noqa: F401 (re-exported for Electrum)
|
||||
Reference in New Issue
Block a user