Merge pull request #9651 from f321x/plugin_manifest_json
Use manifest.json instead of loading init file for plugin registration
This commit is contained in:
@@ -35,12 +35,18 @@ with zipfile.ZipFile(zip_path, 'w') as zip_object:
|
||||
print('added', dest_path)
|
||||
|
||||
# read version
|
||||
zip_file = zipimport.zipimporter(zip_path)
|
||||
module = zip_file.load_module(plugin_name)
|
||||
if not module.version:
|
||||
try:
|
||||
with open(os.path.join(source_dir, 'manifest.json'), 'r') as f:
|
||||
manifest = json.load(f)
|
||||
version = manifest.get('version')
|
||||
except FileNotFoundError:
|
||||
raise Exception(f"plugin doesn't contain manifest.json")
|
||||
|
||||
if not version:
|
||||
raise Exception('version not set')
|
||||
|
||||
versioned_plugin_name = plugin_name + '-' + module.version + '.zip'
|
||||
|
||||
versioned_plugin_name = plugin_name + '-' + version + '.zip'
|
||||
zip_path_with_version = os.path.join(dest_dir, versioned_plugin_name)
|
||||
# rename zip file
|
||||
os.rename(zip_path, zip_path_with_version)
|
||||
|
||||
@@ -1573,9 +1573,7 @@ def plugin_command(s, plugin_name):
|
||||
func.plugin_name = plugin_name
|
||||
name = plugin_name + '_' + func.__name__
|
||||
if name in known_commands or hasattr(Commands, name):
|
||||
# electrum plugins are always loaded before the plugin commands,
|
||||
# so plugin commands cannot overwrite them
|
||||
return
|
||||
raise Exception(f"Command name {name} already exists. Plugin commands should not overwrite other commands.")
|
||||
assert asyncio.iscoroutinefunction(func), f"Plugin commands must be a coroutine: {name}"
|
||||
@command(s)
|
||||
@wraps(func)
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import json
|
||||
import os
|
||||
import pkgutil
|
||||
import importlib.util
|
||||
@@ -31,6 +32,7 @@ import threading
|
||||
import traceback
|
||||
import sys
|
||||
import aiohttp
|
||||
import zipfile as zipfile_lib
|
||||
|
||||
from typing import (NamedTuple, Any, Union, TYPE_CHECKING, Optional, Tuple,
|
||||
Dict, Iterable, List, Sequence, Callable, TypeVar, Mapping)
|
||||
@@ -58,6 +60,7 @@ _logger = get_logger(__name__)
|
||||
plugin_loaders = {}
|
||||
hook_names = set()
|
||||
hooks = {}
|
||||
_root_permission_cache = {}
|
||||
|
||||
|
||||
class Plugins(DaemonThread):
|
||||
@@ -71,11 +74,11 @@ class Plugins(DaemonThread):
|
||||
self.cmd_only = cmd_only # type: bool
|
||||
self.internal_plugin_metadata = {}
|
||||
self.external_plugin_metadata = {}
|
||||
self.loaded_command_modules = set() # type: set[str]
|
||||
if cmd_only:
|
||||
# only import the command modules of plugins
|
||||
Logger.__init__(self)
|
||||
self.find_plugins()
|
||||
self.load_plugins()
|
||||
return
|
||||
DaemonThread.__init__(self)
|
||||
self.device_manager = DeviceMgr(config)
|
||||
@@ -101,46 +104,32 @@ class Plugins(DaemonThread):
|
||||
# we exclude the ones packaged as *code*, here:
|
||||
if loader.__class__.__qualname__ == "PyiFrozenImporter":
|
||||
continue
|
||||
if self.cmd_only and self.config.get('enable_plugin_' + name) is not True:
|
||||
module_path = os.path.join(pkg_path, name)
|
||||
if external and not self._has_recursive_root_permissions(module_path):
|
||||
self.logger.info(f"Not loading plugin {module_path}: directory has user write permissions")
|
||||
continue
|
||||
base_name = 'electrum.plugins' if not external else 'electrum_external_plugins'
|
||||
full_name = f'{base_name}.{name}'
|
||||
if external:
|
||||
module_path = os.path.join(pkg_path, name)
|
||||
if not self._has_recursive_root_permissions(module_path):
|
||||
self.logger.info(f"Not loading plugin {module_path}: directory has user write permissions")
|
||||
continue
|
||||
module_path = os.path.join(module_path, '__init__.py')
|
||||
if not os.path.exists(module_path):
|
||||
continue
|
||||
spec = importlib.util.spec_from_file_location(full_name, module_path)
|
||||
else:
|
||||
spec = importlib.util.find_spec(full_name)
|
||||
if spec is None:
|
||||
if self.cmd_only:
|
||||
continue # no commands module in this plugin
|
||||
raise Exception(f"Error pre-loading {full_name}: no spec")
|
||||
module = self.exec_module_from_spec(spec, full_name)
|
||||
if self.cmd_only:
|
||||
assert name not in self.loaded_command_modules, f"tried to load commands of {name} twice"
|
||||
self.loaded_command_modules.add(name)
|
||||
if self.cmd_only and not self.config.get('enable_plugin_' + name) is True:
|
||||
continue
|
||||
try:
|
||||
with open(os.path.join(module_path, 'manifest.json'), 'r') as f:
|
||||
d = json.load(f)
|
||||
except FileNotFoundError:
|
||||
self.logger.info(f"could not find manifest.json of plugin {name}, skipping...")
|
||||
continue
|
||||
d = module.__dict__
|
||||
if 'fullname' not in d:
|
||||
continue
|
||||
d['display_name'] = d['fullname']
|
||||
gui_good = self.gui_name in d.get('available_for', [])
|
||||
if not gui_good:
|
||||
continue
|
||||
details = d.get('registers_wallet_type')
|
||||
if details:
|
||||
self.register_wallet_type(name, gui_good, details)
|
||||
details = d.get('registers_keystore')
|
||||
if details:
|
||||
self.register_keystore(name, gui_good, details)
|
||||
if d.get('requires_wallet_type'):
|
||||
# trustedcoin will not be added to list
|
||||
continue
|
||||
d['path'] = module_path
|
||||
if not self.cmd_only:
|
||||
gui_good = self.gui_name in d.get('available_for', [])
|
||||
if not gui_good:
|
||||
continue
|
||||
details = d.get('registers_wallet_type')
|
||||
if details:
|
||||
self.register_wallet_type(name, gui_good, details)
|
||||
details = d.get('registers_keystore')
|
||||
if details:
|
||||
self.register_keystore(name, gui_good, details)
|
||||
if name in self.internal_plugin_metadata or name in self.external_plugin_metadata:
|
||||
_logger.info(f"Found the following plugin modules: {iter_modules=}")
|
||||
raise Exception(f"duplicate plugins? for {name=}")
|
||||
@@ -174,7 +163,10 @@ class Plugins(DaemonThread):
|
||||
for name, d in chain(self.internal_plugin_metadata.items(), self.external_plugin_metadata.items()):
|
||||
if not d.get('requires_wallet_type') and self.config.get('enable_plugin_' + name):
|
||||
try:
|
||||
self.load_plugin_by_name(name)
|
||||
if self.cmd_only: # only load init method to register commands
|
||||
self.maybe_load_plugin_init_method(name)
|
||||
else:
|
||||
self.load_plugin_by_name(name)
|
||||
except BaseException as e:
|
||||
self.logger.exception(f"cannot initialize plugin {name}: {e}")
|
||||
|
||||
@@ -184,12 +176,17 @@ class Plugins(DaemonThread):
|
||||
@profiler(min_threshold=0.5)
|
||||
def _has_recursive_root_permissions(self, path):
|
||||
"""Check if a directory and all its subdirectories have root permissions"""
|
||||
global _root_permission_cache
|
||||
if _root_permission_cache.get(path) is not None:
|
||||
return _root_permission_cache[path]
|
||||
_root_permission_cache[path] = False
|
||||
for root, dirs, files in os.walk(path):
|
||||
if not self._has_root_permissions(root):
|
||||
return False
|
||||
for f in files:
|
||||
if not self._has_root_permissions(os.path.join(root, f)):
|
||||
return False
|
||||
_root_permission_cache[path] = True
|
||||
return True
|
||||
|
||||
def get_external_plugin_dir(self):
|
||||
@@ -237,23 +234,25 @@ class Plugins(DaemonThread):
|
||||
raise Exception(f"duplicate plugins for name={name}")
|
||||
if self.cmd_only and not self.config.get('enable_plugin_' + name):
|
||||
continue
|
||||
module_path = f'electrum_external_plugins.{name}' if external else f'electrum.plugins.{name}'
|
||||
spec = zipfile.find_spec(name)
|
||||
module = self.exec_module_from_spec(spec, module_path)
|
||||
if self.cmd_only:
|
||||
assert name not in self.loaded_command_modules, f"tried to load commands of {name} twice"
|
||||
self.loaded_command_modules.add(name)
|
||||
continue
|
||||
d = module.__dict__
|
||||
gui_good = self.gui_name in d.get('available_for', [])
|
||||
if not gui_good:
|
||||
try:
|
||||
with zipfile_lib.ZipFile(path) as file:
|
||||
manifest_path = os.path.join(name, 'manifest.json')
|
||||
with file.open(manifest_path, 'r') as f:
|
||||
d = json.load(f)
|
||||
except Exception:
|
||||
self.logger.info(f"could not load manifest.json from zip plugin {filename}", exc_info=True)
|
||||
continue
|
||||
d['filename'] = filename
|
||||
if 'fullname' not in d:
|
||||
continue
|
||||
d['display_name'] = d['fullname']
|
||||
d['zip_hash_sha256'] = get_file_hash256(path)
|
||||
d['is_zip'] = True
|
||||
d['path'] = path
|
||||
if not self.cmd_only:
|
||||
gui_good = self.gui_name in d.get('available_for', [])
|
||||
if not gui_good:
|
||||
continue
|
||||
if 'fullname' not in d:
|
||||
continue
|
||||
d['display_name'] = d['fullname']
|
||||
d['zip_hash_sha256'] = get_file_hash256(path)
|
||||
if external:
|
||||
self.external_plugin_metadata[name] = d
|
||||
else:
|
||||
@@ -274,11 +273,35 @@ class Plugins(DaemonThread):
|
||||
else:
|
||||
raise Exception(f"could not find plugin {name!r}")
|
||||
|
||||
def load_plugin_by_name(self, name) -> 'BasePlugin':
|
||||
def maybe_load_plugin_init_method(self, name: str) -> None:
|
||||
"""Loads the __init__.py module of the plugin if it is not already loaded."""
|
||||
is_external = name in self.external_plugin_metadata
|
||||
base_name = (f'electrum_external_plugins.' if is_external else 'electrum.plugins.') + name
|
||||
if base_name not in sys.modules:
|
||||
metadata = self.get_metadata(name)
|
||||
is_zip = metadata.get('is_zip', False)
|
||||
# if the plugin was not enabled on startup the init module hasn't been loaded yet
|
||||
if not is_zip:
|
||||
if is_external:
|
||||
path = os.path.join(metadata['path'], '__init__.py')
|
||||
init_spec = importlib.util.spec_from_file_location(base_name, path)
|
||||
else:
|
||||
init_spec = importlib.util.find_spec(base_name)
|
||||
else:
|
||||
zipfile = zipimport.zipimporter(metadata['path'])
|
||||
init_spec = zipfile.find_spec(name)
|
||||
self.exec_module_from_spec(init_spec, base_name)
|
||||
if name == "trustedcoin":
|
||||
# removes trustedcoin after loading to not show it in the list of plugins
|
||||
del self.internal_plugin_metadata[name]
|
||||
|
||||
def load_plugin_by_name(self, name: str) -> 'BasePlugin':
|
||||
if name in self.plugins:
|
||||
return self.plugins[name]
|
||||
|
||||
is_zip = self.is_plugin_zip(name)
|
||||
# if the plugin was not enabled on startup the init module hasn't been loaded yet
|
||||
self.maybe_load_plugin_init_method(name)
|
||||
|
||||
is_external = name in self.external_plugin_metadata
|
||||
if not is_external:
|
||||
full_name = f'electrum.plugins.{name}.{self.gui_name}'
|
||||
@@ -289,11 +312,7 @@ class Plugins(DaemonThread):
|
||||
if spec is None:
|
||||
raise RuntimeError(f"{self.gui_name} implementation for {name} plugin not found")
|
||||
try:
|
||||
if is_zip:
|
||||
module = self.exec_module_from_spec(spec, full_name)
|
||||
else:
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
module = self.exec_module_from_spec(spec, full_name)
|
||||
plugin = module.Plugin(self, self.config, name)
|
||||
except Exception as e:
|
||||
raise Exception(f"Error loading {name} plugin: {repr(e)}") from e
|
||||
@@ -493,10 +512,9 @@ class BasePlugin(Logger):
|
||||
raise NotImplementedError()
|
||||
|
||||
def read_file(self, filename: str) -> bytes:
|
||||
import zipfile
|
||||
if self.parent.is_plugin_zip(self.name):
|
||||
plugin_filename = self.parent.zip_plugin_path(self.name)
|
||||
with zipfile.ZipFile(plugin_filename) as myzip:
|
||||
with zipfile_lib.ZipFile(plugin_filename) as myzip:
|
||||
with myzip.open(os.path.join(self.name, filename)) as myfile:
|
||||
return myfile.read()
|
||||
else:
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
from electrum.i18n import _
|
||||
|
||||
fullname = _('Audio MODEM')
|
||||
description = _('Provides support for air-gapped transaction signing.')
|
||||
requires = [('amodem', 'http://github.com/romanz/amodem/')]
|
||||
available_for = ['qt']
|
||||
|
||||
|
||||
6
electrum/plugins/audio_modem/manifest.json
Normal file
6
electrum/plugins/audio_modem/manifest.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"fullname": "Audio MODEM",
|
||||
"description": "Provides support for air-gapped transaction signing.",
|
||||
"requires": [["amodem", "http://github.com/romanz/amodem/"]],
|
||||
"available_for": ["qt"]
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
from electrum.i18n import _
|
||||
|
||||
fullname = "BitBox02"
|
||||
description = (
|
||||
"Provides support for the BitBox02 hardware wallet"
|
||||
)
|
||||
requires = [
|
||||
(
|
||||
"bitbox02",
|
||||
"https://github.com/digitalbitbox/bitbox02-firmware/tree/master/py/bitbox02",
|
||||
)
|
||||
]
|
||||
registers_keystore = ("hardware", "bitbox02", _("BitBox02"))
|
||||
available_for = ["qt"]
|
||||
|
||||
7
electrum/plugins/bitbox02/manifest.json
Normal file
7
electrum/plugins/bitbox02/manifest.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"fullname": "BitBox02",
|
||||
"description": "Provides support for the BitBox02 hardware wallet",
|
||||
"requires": [["bitbox02", "https://github.com/digitalbitbox/bitbox02-firmware/tree/master/py/bitbox02"]],
|
||||
"registers_keystore": ["hardware", "bitbox02", "BitBox02"],
|
||||
"available_for": ["qt"]
|
||||
}
|
||||
@@ -1,7 +1 @@
|
||||
from electrum.i18n import _
|
||||
|
||||
fullname = 'Coldcard Wallet'
|
||||
description = 'Provides support for the Coldcard hardware wallet from Coinkite'
|
||||
requires = [('ckcc-protocol', 'github.com/Coldcard/ckcc-protocol')]
|
||||
registers_keystore = ('hardware', 'coldcard', _("Coldcard Wallet"))
|
||||
available_for = ['qt', 'cmdline']
|
||||
|
||||
7
electrum/plugins/coldcard/manifest.json
Normal file
7
electrum/plugins/coldcard/manifest.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"fullname": "Coldcard Wallet",
|
||||
"description": "Provides support for the Coldcard hardware wallet from Coinkite",
|
||||
"requires": [["ckcc-protocol", "github.com/Coldcard/ckcc-protocol"]],
|
||||
"registers_keystore": ["hardware", "coldcard", "Coldcard Wallet"],
|
||||
"available_for": ["qt", "cmdline"]
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
from electrum.i18n import _
|
||||
|
||||
fullname = 'Digital Bitbox'
|
||||
description = _('Provides support for Digital Bitbox hardware wallet')
|
||||
registers_keystore = ('hardware', 'digitalbitbox', _("Digital Bitbox wallet"))
|
||||
available_for = ['qt', 'cmdline']
|
||||
|
||||
6
electrum/plugins/digitalbitbox/manifest.json
Normal file
6
electrum/plugins/digitalbitbox/manifest.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"fullname": "Digital Bitbox",
|
||||
"description": "Provides support for Digital Bitbox hardware wallet",
|
||||
"registers_keystore": ["hardware", "digitalbitbox", "Digital Bitbox wallet"],
|
||||
"available_for": ["qt", "cmdline"]
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
from electrum.i18n import _
|
||||
|
||||
fullname = 'Blockstream Jade Wallet'
|
||||
description = 'Provides support for the Blockstream Jade hardware wallet'
|
||||
#requires = [('', 'github.com/')]
|
||||
registers_keystore = ('hardware', 'jade', _("Jade wallet"))
|
||||
available_for = ['qt', 'cmdline']
|
||||
|
||||
6
electrum/plugins/jade/manifest.json
Normal file
6
electrum/plugins/jade/manifest.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"fullname": "Blockstream Jade Wallet",
|
||||
"description": "Provides support for the Blockstream Jade hardware wallet",
|
||||
"registers_keystore": ["hardware", "jade", "Jade wallet"],
|
||||
"available_for": ["qt", "cmdline"]
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
from electrum.i18n import _
|
||||
|
||||
fullname = 'KeepKey'
|
||||
description = _('Provides support for KeepKey hardware wallet')
|
||||
requires = [('keepkeylib','github.com/keepkey/python-keepkey')]
|
||||
registers_keystore = ('hardware', 'keepkey', _("KeepKey wallet"))
|
||||
available_for = ['qt', 'cmdline']
|
||||
|
||||
7
electrum/plugins/keepkey/manifest.json
Normal file
7
electrum/plugins/keepkey/manifest.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"fullname": "KeepKey",
|
||||
"description": "Provides support for KeepKey hardware wallet",
|
||||
"requires": [["keepkeylib", "github.com/keepkey/python-keepkey"]],
|
||||
"registers_keystore": ["hardware", "keepkey", "KeepKey wallet"],
|
||||
"available_for": ["qt", "cmdline"]
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
from electrum.i18n import _
|
||||
from electrum.commands import plugin_command
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
@@ -6,14 +5,6 @@ if TYPE_CHECKING:
|
||||
from .labels import LabelsPlugin
|
||||
from electrum.commands import Commands
|
||||
|
||||
|
||||
fullname = _('LabelSync')
|
||||
description = ' '.join([
|
||||
_("Save your wallet labels on a remote server, and synchronize them across multiple devices where you use Electrum."),
|
||||
_("Labels, transactions IDs and addresses are encrypted before they are sent to the remote server.")
|
||||
])
|
||||
available_for = ['qt', 'qml', 'cmdline']
|
||||
|
||||
plugin_name = "labels"
|
||||
|
||||
@plugin_command('w', plugin_name)
|
||||
|
||||
5
electrum/plugins/labels/manifest.json
Normal file
5
electrum/plugins/labels/manifest.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"fullname": "LabelSync",
|
||||
"description": "Save your wallet labels on a remote server, and synchronize them across multiple devices where you use Electrum. Labels, transactions IDs and addresses are encrypted before they are sent to the remote server.",
|
||||
"available_for": ["qt", "qml", "cmdline"]
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
from electrum.i18n import _
|
||||
|
||||
fullname = 'Ledger Wallet'
|
||||
description = 'Provides support for Ledger hardware wallet'
|
||||
requires = [('ledger_bitcoin', 'github.com/LedgerHQ/app-bitcoin-new')]
|
||||
registers_keystore = ('hardware', 'ledger', _("Ledger wallet"))
|
||||
available_for = ['qt', 'cmdline']
|
||||
|
||||
7
electrum/plugins/ledger/manifest.json
Normal file
7
electrum/plugins/ledger/manifest.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"fullname": "Ledger Wallet",
|
||||
"description": "Provides support for Ledger hardware wallet",
|
||||
"requires": [["ledger_bitcoin", "github.com/LedgerHQ/app-bitcoin-new"]],
|
||||
"registers_keystore": ["hardware", "ledger", "Ledger wallet"],
|
||||
"available_for": ["qt", "cmdline"]
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
from electrum.i18n import _
|
||||
|
||||
fullname = _('PayServer')
|
||||
description = 'run a HTTP server for receiving payments'
|
||||
available_for = ['cmdline']
|
||||
|
||||
5
electrum/plugins/payserver/manifest.json
Normal file
5
electrum/plugins/payserver/manifest.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"fullname": "PayServer",
|
||||
"description": "run a HTTP server for receiving payments",
|
||||
"available_for": ["cmdline"]
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
from electrum.i18n import _
|
||||
fullname = _('PSBT over Nostr')
|
||||
description = ' '.join([
|
||||
_("This plugin facilitates the use of multi-signatures wallets."),
|
||||
_("It sends and receives partially signed transactions from/to your cosigner wallet."),
|
||||
_("PSBTs are sent and retrieved from Nostr relays.")
|
||||
])
|
||||
author = 'The Electrum Developers'
|
||||
#requires_wallet_type = ['2of2', '2of3']
|
||||
available_for = ['qt']
|
||||
version = '0.0.1'
|
||||
|
||||
7
electrum/plugins/psbt_nostr/manifest.json
Normal file
7
electrum/plugins/psbt_nostr/manifest.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"fullname": "PSBT over Nostr",
|
||||
"description": "This plugin facilitates the use of multi-signatures wallets. It sends and receives partially signed transactions from/to your cosigner wallet. PSBTs are sent and retrieved from Nostr relays.",
|
||||
"author": "The Electrum Developers",
|
||||
"available_for": ["qt"],
|
||||
"version": "0.0.1"
|
||||
}
|
||||
@@ -1,9 +1,2 @@
|
||||
from electrum.i18n import _
|
||||
|
||||
fullname = _('Revealer Backup Utility')
|
||||
description = ''.join(["<br/>",
|
||||
"<b>"+_("Do you have something to hide ?")+"</b>", '<br/>', '<br/>',
|
||||
_("This plug-in allows you to create a visually encrypted backup of your wallet seeds, or of custom alphanumeric secrets."), '<br/>'])
|
||||
available_for = ['qt']
|
||||
|
||||
|
||||
|
||||
5
electrum/plugins/revealer/manifest.json
Normal file
5
electrum/plugins/revealer/manifest.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"fullname": "Revealer Backup Utility",
|
||||
"description": "<br/><b>Do you have something to hide ?</b><br/><br/>This plug-in allows you to create a visually encrypted backup of your wallet seeds, or of custom alphanumeric secrets.<br/>",
|
||||
"available_for": ["qt"]
|
||||
}
|
||||
@@ -1,8 +1 @@
|
||||
from electrum.i18n import _
|
||||
|
||||
fullname = 'Safe-T mini Wallet'
|
||||
description = _('Provides support for Safe-T mini hardware wallet')
|
||||
requires = [('safetlib','github.com/archos-safe-t/python-safet')]
|
||||
registers_keystore = ('hardware', 'safe_t', _("Safe-T mini wallet"))
|
||||
available_for = ['qt', 'cmdline']
|
||||
|
||||
|
||||
7
electrum/plugins/safe_t/manifest.json
Normal file
7
electrum/plugins/safe_t/manifest.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"fullname": "Safe-T mini Wallet",
|
||||
"description": "Provides support for Safe-T mini hardware wallet",
|
||||
"requires": [["safetlib", "github.com/archos-safe-t/python-safet"]],
|
||||
"registers_keystore": ["hardware", "safe_t", "Safe-T mini wallet"],
|
||||
"available_for": ["qt", "cmdline"]
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
from electrum.i18n import _
|
||||
|
||||
fullname = _('SwapServer')
|
||||
description = """
|
||||
Submarine swap server for an Electrum daemon.
|
||||
|
||||
Example setup:
|
||||
|
||||
electrum -o setconfig enable_plugin_swapserver True
|
||||
electrum -o setconfig swapserver_port 5455
|
||||
electrum daemon -v
|
||||
|
||||
"""
|
||||
|
||||
available_for = ['cmdline']
|
||||
|
||||
5
electrum/plugins/swapserver/manifest.json
Normal file
5
electrum/plugins/swapserver/manifest.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"fullname": "SwapServer",
|
||||
"description": "Submarine swap server for an Electrum daemon.\n\nExample setup:\n\n electrum -o setconfig enable_plugin_swapserver True\n electrum -o setconfig swapserver_port 5455\n electrum daemon -v\n\n",
|
||||
"available_for": ["cmdline"]
|
||||
}
|
||||
@@ -1,8 +1 @@
|
||||
from electrum.i18n import _
|
||||
|
||||
fullname = 'Trezor Wallet'
|
||||
description = _('Provides support for Trezor hardware wallet')
|
||||
requires = [('trezorlib','pypi.org/project/trezor/')]
|
||||
registers_keystore = ('hardware', 'trezor', _("Trezor wallet"))
|
||||
available_for = ['qt', 'cmdline']
|
||||
|
||||
|
||||
7
electrum/plugins/trezor/manifest.json
Normal file
7
electrum/plugins/trezor/manifest.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"fullname": "Trezor Wallet",
|
||||
"description": "Provides support for Trezor hardware wallet",
|
||||
"requires": [["trezorlib","pypi.org/project/trezor/"]],
|
||||
"registers_keystore": ["hardware", "trezor", "Trezor wallet"],
|
||||
"available_for": ["qt", "cmdline"]
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
from electrum.i18n import _
|
||||
|
||||
fullname = _('Two Factor Authentication')
|
||||
description = ''.join([
|
||||
_("This plugin adds two-factor authentication to your wallet."), '<br/>',
|
||||
_("For more information, visit"),
|
||||
" <a href=\"https://api.trustedcoin.com/#/electrum-help\">https://api.trustedcoin.com/#/electrum-help</a>"
|
||||
])
|
||||
requires_wallet_type = ['2fa']
|
||||
registers_wallet_type = '2fa'
|
||||
available_for = ['qt', 'cmdline', 'qml']
|
||||
|
||||
7
electrum/plugins/trustedcoin/manifest.json
Normal file
7
electrum/plugins/trustedcoin/manifest.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"fullname": "Two Factor Authentication",
|
||||
"description": "This plugin adds two-factor authentication to your wallet.<br/>For more information, visit <a href=\"https://api.trustedcoin.com/#/electrum-help\">https://api.trustedcoin.com/#/electrum-help</a>",
|
||||
"requires_wallet_type": ["2fa"],
|
||||
"registers_wallet_type": "2fa",
|
||||
"available_for": ["qt", "cmdline", "qml"]
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
from electrum.i18n import _
|
||||
|
||||
fullname = _('Watchtower')
|
||||
description = """
|
||||
A watchtower is a daemon that watches your channels and prevents the other party from stealing funds by broadcasting an old state.
|
||||
|
||||
Example:
|
||||
|
||||
daemon setup:
|
||||
|
||||
electrum -o setconfig enable_plugin_watchtower True
|
||||
electrum -o setconfig watchtower_user wtuser
|
||||
electrum -o setconfig watchtower_password wtpassword
|
||||
electrum -o setconfig watchtower_port 12345
|
||||
electrum daemon -v
|
||||
|
||||
client setup:
|
||||
|
||||
electrum -o setconfig watchtower_url http://wtuser:wtpassword@127.0.0.1:12345
|
||||
|
||||
"""
|
||||
|
||||
available_for = ['cmdline']
|
||||
|
||||
5
electrum/plugins/watchtower/manifest.json
Normal file
5
electrum/plugins/watchtower/manifest.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"fullname": "Watchtower",
|
||||
"description": "A watchtower is a daemon that watches your channels and prevents the other party from stealing funds by broadcasting an old state.\n\nExample:\n\ndaemon setup:\n\n electrum -o setconfig enable_plugin_watchtower True\n electrum -o setconfig watchtower_user wtuser\n electrum -o setconfig watchtower_password wtpassword\n electrum -o setconfig watchtower_port 12345\n electrum daemon -v\n\nclient setup:\n\n electrum -o setconfig watchtower_url http://wtuser:wtpassword@127.0.0.1:12345\n\n",
|
||||
"available_for": ["cmdline"]
|
||||
}
|
||||
Reference in New Issue
Block a user