129 Commits

Author SHA1 Message Date
copronista
6be8e3ec88 Update plugin.py
https://github.com/spesmilo/electrum/issues/10220
2025-09-13 23:12:44 -04:00
Sander van Grieken
2ca9e1b686 plugin: fix translatable string, and organize whitespace/imports/type hints while weŕe at it. 2025-08-14 11:37:16 +02:00
ThomasV
d7f6820fd6 hardware_wallets: register keystore if if external plugin was authorized
Also remove 'gui_good' from hw_wallets dict (was always True)
2025-06-19 11:23:57 +02:00
ThomasV
630124136e plugins dialog: fix is_available, do not show plugins that are not available 2025-06-10 11:02:18 +02:00
f321x
18e241edf0 fix: use make_aiohttp_session for plugin dl 2025-06-06 17:24:41 +02:00
ThomasV
853b793bef rm verbosity_shortcuts option (unused, redundant) 2025-05-29 16:20:41 +02:00
SomberNight
37350edeae plugins: use util.make_dir for creating external plugin dir
This sets more restrictive unix permissions on the folder.
Also, util.make_dir is "multiprocess-safe".
2025-05-23 15:49:14 +00:00
SomberNight
9b24316915 plugin: _execute_commands_in_subprocess: make sure pipes get closed 2025-05-22 21:48:27 +00:00
ThomasV
79941529d2 simplify plugin logic: remove install/uninstall buttons
external plugins are enabled iff authorized
2025-05-18 12:24:38 +02:00
f321x
abc50a97e1 plugins: add functionality to allow setting plugin pubkey from gui
Adds functionality that allows the user to store the plugin authorization pubkey without having to edit files/registry manually.
On Linux systems it spawns the commands in a subprocess with pkexec which will trigger a OS prompt to execute the commands as root.
The user sees the executed commands and can either authorize with the root password or decline.
On windows it uses the windows `ShellExecuteExW` api to edit the registry, this also triggers a OS dialog to accept or decline (UAC dialog).
There is also functionality to reset the key again, which works in the same way.
2025-05-09 13:34:09 +02:00
SomberNight
ba3783f998 refactor qt.util.ChoiceWidget: introduce ChoiceItem 2025-05-06 18:12:37 +00:00
SomberNight
ef49bb2109 hw plugins: fix DeviceMgr.select_device
regression from ChoiceWidget refactor

follow-up f7749d62aa
2025-05-06 18:11:10 +00:00
f321x
e80551192b plugins: structure plugin storage in wallet
store all plugin data by plugin name in a root dictionary `plugin_data`
inside the wallet db so that plugin data can get deleted again.
Prunes the data of plugins from the wallet db on wallet stop if the
plugin is not installed anymore.
2025-05-06 13:16:49 +02:00
ThomasV
9ce21173ec Merge pull request #9770 from f321x/plugin_update
plugins: handle updating plugins from plugin manager
2025-04-30 14:17:01 +02:00
f321x
3bfe1d2d28 handle loading plugins with same name as installed plugin (update) 2025-04-29 15:30:26 +02:00
f321x
1cce216c1f fix: prevent PluginsDialog from getting in bad state
If the plugin file got already deleted while being in the installation
dialog, trying to delete it again will raise an exception.
This is fixed by catching the exception.

If the user tries to install an external plugin that is already
installed, and then closes the PluginDialog, the PluginsDialog will
get into a bad state, throwing an exeption when opening it. This
happens because the add_plugin_dialog deletes the zipfile if the
user closes or cancels the installation dialog.
This is fixed by checking if the plugin is already existing,
instead of trying to install an already existing plugin.
2025-04-29 13:09:19 +02:00
ThomasV
8c028f7528 Add/remove plugins from GUI
- both internal and external plugins require GUI install
   (except internal HW plugins, which are 'auto-loaded' and hidden)
 - remove init_qt hook
 - in Qt, reload wallet windows if plugin enabled/disabled
 - add 'uninstall' button to PluginDialog
 - add 'add plugins' button to wizard hw screen
 - add icons to the plugin list
2025-04-15 08:35:10 +02:00
ThomasV
8f3490c87e recursive config file
move plugin variables into sub dictionaries of user config
2025-04-11 19:06:48 +02:00
ThomasV
e084789577 minor fix (follow-up 737417fb80) 2025-04-11 10:05:58 +02:00
ThomasV
de047195a9 Allow zip plugins to register keystore
This makes it possible to create external plugins that add support
for hardware wallets.
2025-04-11 09:20:57 +02:00
ThomasV
737417fb80 Userspace plugins:
- Allow plugins saved as zipfiles in user data dir
 - plugins are authorized with a user chosen password
 - pubkey derived from password is saved with admin permissions
2025-04-11 08:45:28 +02:00
ThomasV
6e087950cf move hw_wallet.py from plugins to electrum library 2025-04-10 10:19:15 +02:00
SomberNight
5c233ac325 ci: enable more flake8 stuff
```
$ export ELECTRUM_LINTERS=E9,E101,E129,E273,E274,E703,E71,E722,F5,F6,F7,F8,W191,W29,B
$ export ELECTRUM_LINTERS_IGNORE=B007,B009,B010,B019,B036,F541,F841
$ flake8 . --count --select="$ELECTRUM_LINTERS" --ignore="$ELECTRUM_LINTERS_IGNORE" --show-source --statistics --exclude "*_pb2.py,electrum/_vendor/"
./electrum/commands.py:98:1: F811 redefinition of unused 'format_satoshis' from line 48
def format_satoshis(x):
^
./electrum/commands.py:437:9: F811 redefinition of unused 'Mnemonic' from line 62
        from .mnemonic import Mnemonic
        ^
./electrum/gui/qt/wizard/wallet.py:37:5: F811 redefinition of unused 'Daemon' from line 14
    from electrum.daemon import Daemon
    ^
./electrum/lntransport.py:14:1: F811 redefinition of unused 'Optional' from line 12
from typing import NamedTuple, List, Tuple, Mapping, Optional, TYPE_CHECKING, Union, Dict, Set, Sequence
^
./electrum/lntransport.py:14:1: F811 redefinition of unused 'TYPE_CHECKING' from line 12
from typing import NamedTuple, List, Tuple, Mapping, Optional, TYPE_CHECKING, Union, Dict, Set, Sequence
^
./electrum/plugin.py:966:13: F811 redefinition of unused 'hid' from line 593
            import hid
            ^
./electrum/plugin.py:1040:13: F811 redefinition of unused 'hid' from line 593
            import hid
            ^
./electrum/util.py:44:1: F811 redefinition of unused 'json' from line 26
import json
^
./electrum/util.py:46:1: F811 redefinition of unused 'NamedTuple' from line 29
from typing import NamedTuple, Optional
^
./electrum/util.py:46:1: F811 redefinition of unused 'Optional' from line 29
from typing import NamedTuple, Optional
^
./electrum/util.py:1456:56: F811 redefinition of unused 'traceback' from line 34
        async def __aexit__(self, exc_type, exc_value, traceback):
                                                       ^
./electrum/wallet_db.py:536:9: F811 redefinition of unused 'LOCAL' from line 46
        LOCAL = 1
        ^
./electrum/wallet_db.py:537:9: F811 redefinition of unused 'REMOTE' from line 46
        REMOTE = -1
        ^
./tests/test_bitcoin.py:28:1: F811 redefinition of unused 'bitcoin' from line 9
from electrum import crypto, constants, bitcoin
^
./tests/test_txbatcher.py:11:1: F811 redefinition of unused 'Transaction' from line 7
from electrum.transaction import Transaction, PartialTxInput, PartialTxOutput, TxOutpoint
^
./tests/test_wallet_vertical.py:20:1: F811 redefinition of unused 'Transaction' from line 10
from electrum.transaction import Transaction, PartialTxOutput, tx_from_any, Sighash
^
16    F811 redefinition of unused 'format_satoshis' from line 48
16

```
2025-04-02 16:21:59 +00:00
SomberNight
55281295b7 ci: bump flake8 to new version
```
./electrum/commands.py:144:9: F824 `global known_commands` is unused: name is never assigned in scope
        global known_commands
        ^
./electrum/commands.py:1916:9: F824 `global known_commands` is unused: name is never assigned in scope
        global known_commands
        ^
./electrum/gui/qt/main_window.py:2405:13: F824 `nonlocal done` is unused: name is never assigned in scope
            nonlocal done
            ^
./electrum/i18n.py:52:5: F824 `global language` is unused: name is never assigned in scope
    global language
    ^
./electrum/plugin.py:189:9: F824 `global _root_permission_cache` is unused: name is never assigned in scope
        global _root_permission_cache
        ^
5     F824 `global known_commands` is unused: name is never assigned in scope
5

```
2025-04-02 14:56:53 +00:00
ThomasV
4c14711dc7 Merge pull request #9656 from SomberNight/202503_configvar_plugins
plugin ConfigVars: define vars less dynamically
2025-03-20 08:45:40 +01:00
SomberNight
b132e357a3 plugin ConfigVars: define vars less dynamically
and restore ability to have different internal ConfigVar name and user-visible "key"
(Keys are hard to change as that breaks compat, but it is nice to be able to change
the internal var name, to reorganise stuff sometimes. After new ConfigVars are added,
sometimes we get better insight into how the older ones should have been named.)

follow-up https://github.com/spesmilo/electrum/pull/9648
2025-03-19 16:42:51 +00:00
SomberNight
83bf760c4a plugins: better handle exceptions in __init__
When importing a plugin, if it raised an exception in its `__init__` file, we
ignored it, and still loaded the plugin, in a potentially half-broken state.
This is because maybe_load_plugin_init_method only calls exec_module_from_spec
if the plugin is not already in sys.modules, but exec_module_from_spec
will put the plugin into sys.modules even if it errors.

Consider this patch to test with, enable the "labels" plugin:
```patch
diff --git a/electrum/plugins/labels/__init__.py b/electrum/plugins/labels/__init__.py
index b68127df8e..0d6d95abce 100644
--- a/electrum/plugins/labels/__init__.py
+++ b/electrum/plugins/labels/__init__.py
@@ -21,3 +21,5 @@ async def pull(self: 'Commands', plugin: 'LabelsPlugin' = None, wallet=None, for
     arg:bool:force:pull all labels
     """
     return await plugin.pull_thread(wallet, force=force)
+
+raise Exception("heyheyhey")

```

I would expect we don't load the labels plugin due to the error, but we do:
```
>>> plugins.get_plugin("labels")
<electrum.plugins.labels.qt.Plugin object at 0x7801df30fb50>
```

Log:
```
$ ./run_electrum -v --testnet -o
  0.75 | I | simple_config.SimpleConfig | electrum directory /home/user/.electrum/testnet
  0.75 | E | p/plugin.Plugins | cannot initialize plugin labels: Error pre-loading electrum.plugins.labels: Exception('heyheyhey')
Traceback (most recent call last):
  File "/home/user/wspace/electrum/electrum/plugin.py", line 148, in exec_module_from_spec
    spec.loader.exec_module(module)
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/user/wspace/electrum/electrum/plugins/labels/__init__.py", line 25, in <module>
    raise Exception("heyheyhey")
Exception: heyheyhey

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/user/wspace/electrum/electrum/plugin.py", line 167, in load_plugins
    self.maybe_load_plugin_init_method(name)
  File "/home/user/wspace/electrum/electrum/plugin.py", line 293, in maybe_load_plugin_init_method
    module = self.exec_module_from_spec(init_spec, base_name)
  File "/home/user/wspace/electrum/electrum/plugin.py", line 150, in exec_module_from_spec
    raise Exception(f"Error pre-loading {path}: {repr(e)}") from e
Exception: Error pre-loading electrum.plugins.labels: Exception('heyheyhey')
  0.75 | D | util.profiler | Plugins.__init__ 0.0030 sec
  0.84 | I | simple_config.SimpleConfig | electrum directory /home/user/.electrum/testnet
  0.89 | I | __main__ | get_default_language: detected default as lang='en_UK'
  0.89 | I | i18n | setting language to 'en_UK'
  0.89 | I | logging | Electrum version: 4.5.8 - https://electrum.org - https://github.com/spesmilo/electrum
  0.89 | I | logging | Python version: 3.10.12 (main, Feb  4 2025, 14:57:36) [GCC 11.4.0]. On platform: Linux-6.8.0-52-generic-x86_64-with-glibc2.35
  0.89 | I | logging | Logging to file: /home/user/.electrum/testnet/logs/electrum_log_20250319T161247Z_6605.log
  0.89 | I | logging | Log filters: verbosity '*', verbosity_shortcuts ''
  0.89 | I | exchange_rate.FxThread | using exchange CoinGecko
  0.90 | D | util.profiler | Daemon.__init__ 0.0047 sec
  0.90 | I | daemon.Daemon | starting taskgroup.
  0.90 | I | daemon.CommandsServer | now running and listening. socktype=unix, addr=/home/user/.electrum/testnet/daemon_rpc_socket
  0.90 | I | p/plugin.Plugins | registering hardware bitbox02: ['hardware', 'bitbox02', 'BitBox02']
  0.90 | I | p/plugin.Plugins | registering hardware coldcard: ['hardware', 'coldcard', 'Coldcard Wallet']
  0.90 | I | p/plugin.Plugins | registering hardware digitalbitbox: ['hardware', 'digitalbitbox', 'Digital Bitbox wallet']
  0.90 | I | p/plugin.Plugins | could not find manifest.json of plugin hw_wallet, skipping...
  0.90 | I | p/plugin.Plugins | registering hardware jade: ['hardware', 'jade', 'Jade wallet']
  0.90 | I | p/plugin.Plugins | registering hardware keepkey: ['hardware', 'keepkey', 'KeepKey wallet']
  0.90 | I | p/plugin.Plugins | registering hardware ledger: ['hardware', 'ledger', 'Ledger wallet']
  0.90 | I | p/plugin.Plugins | registering hardware safe_t: ['hardware', 'safe_t', 'Safe-T mini wallet']
  0.90 | I | p/plugin.Plugins | registering hardware trezor: ['hardware', 'trezor', 'Trezor wallet']
  0.90 | I | p/plugin.Plugins | registering wallet type ('2fa', 'trustedcoin')
  1.01 | I | p/plugin.Plugins | loaded plugin 'labels'. (from thread: 'GUI')
  1.01 | D | util.profiler | Plugins.__init__ 0.1183 sec
```
2025-03-19 16:27:23 +00:00
ThomasV
d8964a00e7 config vars for plugins 2025-03-19 11:59:05 +01:00
f321x
a9f8048251 use manifest.json instead of loading init file for plugin registration 2025-03-19 10:38:20 +01:00
ThomasV
38f9cac48c Merge pull request #9649 from f321x/move_commands_to_init
Move plugin commands to init file of plugin
2025-03-18 11:14:30 +01:00
f321x
e74029c880 move plugin commands to init file of plugin 2025-03-18 09:37:07 +01:00
SomberNight
457979ce63 plugin.py: fix plugin.read_file
follow-up 246f03fe20
2025-03-17 17:52:31 +00:00
f321x
246f03fe20 allow all plugins to be either zip or directory based 2025-03-17 16:27:33 +01:00
ThomasV
cb39737a39 Plugins call with cmd_only:
- pass temporary config to Plugins
 - load only enabled plugins
 - parse the command line again after plugins are loaded
2025-03-15 13:31:00 +01:00
f321x
ae64583ebc add handling of plugin commands 2025-03-15 13:22:28 +01:00
SomberNight
be2cd02e54 some clean-ups now that we require python 3.10 2025-01-10 18:52:53 +00:00
f321x
ea10c7cfc1 add filehash of external plugins to PluginDialog
remove hashlib import

add filehash of external plugins to PluginDialog

add emptyline

add filehash of external plugins to PluginDialog
2025-01-09 18:15:12 +01:00
ThomasV
eccc5900e0 move plugin icons to plugins 2024-10-17 13:32:30 +02:00
ThomasV
d70996082e load_external_plugin: allow 'requires_wallet_type' 2024-10-17 10:47:11 +02:00
ThomasV
4ec3b7f344 find_external_plugins: fix for python versions < 3.10 2024-10-11 11:26:33 +02:00
ThomasV
333b3db8ea Unix: Import external plugins from /opt/electrum_plugins 2024-10-08 10:32:37 +02:00
ThomasV
7c6ff6757c plugins dialog: show description and enable buttons in the same dialog 2024-10-02 11:16:59 +02:00
ThomasV
b7a9e4cf7e load_external_plugins: fix config variable name 2024-09-27 14:14:03 +02:00
Sander van Grieken
83e14794a1 plugin: clean up imports, style 2024-06-19 11:24:13 +02:00
SomberNight
85af0b8030 win/mac build: bump pyinstaller (5.11.0->5.13.2)
- needed for bumping python version, as 3.11+ is borked without https://github.com/pyinstaller/pyinstaller/issues/7692
- plugin.py: adapt to pyinstaller 5.12+
    loader was renamed in b9111db8a8
2024-04-18 18:16:10 +00:00
SomberNight
8d07672345 plugin: load_plugin: better exception msg if not found 2024-04-18 16:53:48 +00:00
ThomasV
c9820aeca0 Qt: add download_plugin_dialog 2024-04-13 11:35:49 +02:00
ThomasV
ad774a49be external plugins: add methods to read image files 2024-04-13 11:35:49 +02:00
ThomasV
858d999d31 Allow external plugins
- borrows code brom ElectronCash
 - external plugins are imported as zip files
 - check hash from plugins.json file
2024-04-13 11:35:49 +02:00
ThomasV
3e7d4749cf turn classmethod 'find_all_plugins' into an instance method.
change 'use_' prefix to 'enable_plugin_'
2024-04-13 11:35:49 +02:00