mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-23 17:47:31 +00:00
Break out the workflow logic of the install wizard into a base class. This means reimplementing with full support in a new GUI is now easy; you just provide ways to request passwords, show messages etc. The API is fully documented in the base class. There are a couple of minor outstanding issues, including that the old messages shown when recovering a wallet are missing. I will come back to that. Ledger wallet might be broken. Other improvements: The install wizard code is now easy to follow and understand. Hardware wallets can now be restored without any need for their accompanying libraries. Various bits of trustedcoin were broken and have been fixed. Many plugin hooks can be removed. I have only started on this.
215 lines
6.4 KiB
Python
215 lines
6.4 KiB
Python
#!/usr/bin/env python
|
|
#
|
|
# Electrum - lightweight Bitcoin client
|
|
# Copyright (C) 2015 Thomas Voegtlin
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import traceback
|
|
import sys
|
|
import os
|
|
import imp
|
|
import pkgutil
|
|
import time
|
|
|
|
from util import *
|
|
from i18n import _
|
|
from util import profiler, PrintError, DaemonThread
|
|
import wallet
|
|
|
|
class Plugins(DaemonThread):
|
|
|
|
@profiler
|
|
def __init__(self, config, is_local, gui_name):
|
|
DaemonThread.__init__(self)
|
|
if is_local:
|
|
find = imp.find_module('plugins')
|
|
plugins = imp.load_module('electrum_plugins', *find)
|
|
else:
|
|
plugins = __import__('electrum_plugins')
|
|
self.pkgpath = os.path.dirname(plugins.__file__)
|
|
self.config = config
|
|
self.hw_wallets = {}
|
|
self.plugins = {}
|
|
self.gui_name = gui_name
|
|
self.descriptions = []
|
|
for loader, name, ispkg in pkgutil.iter_modules([self.pkgpath]):
|
|
m = loader.find_module(name).load_module(name)
|
|
d = m.__dict__
|
|
gui_good = gui_name in d.get('available_for', [])
|
|
details = d.get('registers_wallet_type')
|
|
if details:
|
|
self.register_plugin_wallet(name, gui_good, details)
|
|
if not gui_good:
|
|
continue
|
|
self.descriptions.append(d)
|
|
if not d.get('requires_wallet_type') and config.get('use_' + name):
|
|
self.load_plugin(config, name)
|
|
|
|
def get(self, name):
|
|
return self.plugins.get(name)
|
|
|
|
def count(self):
|
|
return len(self.plugins)
|
|
|
|
def load_plugin(self, config, name):
|
|
full_name = 'electrum_plugins.' + name + '.' + self.gui_name
|
|
try:
|
|
p = pkgutil.find_loader(full_name).load_module(full_name)
|
|
plugin = p.Plugin(self, config, name)
|
|
self.add_jobs(plugin.thread_jobs())
|
|
self.plugins[name] = plugin
|
|
self.print_error("loaded", name)
|
|
return plugin
|
|
except Exception:
|
|
print_msg(_("Error: cannot initialize plugin"), name)
|
|
traceback.print_exc(file=sys.stdout)
|
|
return None
|
|
|
|
def close_plugin(self, plugin):
|
|
self.remove_jobs(plugin.thread_jobs())
|
|
|
|
def toggle_enabled(self, config, name):
|
|
p = self.get(name)
|
|
config.set_key('use_' + name, p is None, True)
|
|
if p:
|
|
self.plugins.pop(name)
|
|
p.close()
|
|
self.print_error("closed", name)
|
|
return None
|
|
return self.load_plugin(config, name)
|
|
|
|
def is_available(self, name, w):
|
|
for d in self.descriptions:
|
|
if d.get('__name__') == name:
|
|
break
|
|
else:
|
|
return False
|
|
deps = d.get('requires', [])
|
|
for dep, s in deps:
|
|
try:
|
|
__import__(dep)
|
|
except ImportError:
|
|
return False
|
|
return w.wallet_type in d.get('requires_wallet_type', [])
|
|
|
|
def hardware_wallets(self, action):
|
|
result = []
|
|
for name, (gui_good, details) in self.hw_wallets.items():
|
|
if gui_good:
|
|
try:
|
|
p = self.wallet_plugin_loader(name)
|
|
if action == 'restore' or p.is_enabled():
|
|
result.append((details[1], details[2]))
|
|
except:
|
|
self.print_error("cannot load plugin for:", name)
|
|
return result
|
|
|
|
def register_plugin_wallet(self, name, gui_good, details):
|
|
if details[0] == 'hardware':
|
|
self.hw_wallets[name] = (gui_good, details)
|
|
register = details + (lambda: self.wallet_plugin_loader(name),)
|
|
wallet.wallet_types.append(register)
|
|
|
|
def wallet_plugin_loader(self, name):
|
|
if not name in self.plugins:
|
|
self.load_plugin(self.config, name)
|
|
return self.plugins[name]
|
|
|
|
def run(self):
|
|
jobs = [job for plugin in self.plugins.values()
|
|
for job in plugin.thread_jobs()]
|
|
self.add_jobs(jobs)
|
|
while self.is_running():
|
|
time.sleep(0.1)
|
|
self.run_jobs()
|
|
self.print_error("stopped")
|
|
|
|
|
|
hook_names = set()
|
|
hooks = {}
|
|
|
|
def hook(func):
|
|
hook_names.add(func.func_name)
|
|
return func
|
|
|
|
def run_hook(name, *args):
|
|
return _run_hook(name, False, *args)
|
|
|
|
def always_hook(name, *args):
|
|
return _run_hook(name, True, *args)
|
|
|
|
def _run_hook(name, always, *args):
|
|
results = []
|
|
f_list = hooks.get(name, [])
|
|
for p, f in f_list:
|
|
if always or p.is_enabled():
|
|
try:
|
|
r = f(*args)
|
|
except Exception:
|
|
print_error("Plugin error")
|
|
traceback.print_exc(file=sys.stdout)
|
|
r = False
|
|
if r:
|
|
results.append(r)
|
|
|
|
if results:
|
|
assert len(results) == 1, results
|
|
return results[0]
|
|
|
|
|
|
class BasePlugin(PrintError):
|
|
|
|
def __init__(self, parent, config, name):
|
|
self.parent = parent # The plugins object
|
|
self.name = name
|
|
self.config = config
|
|
self.wallet = None
|
|
# add self to hooks
|
|
for k in dir(self):
|
|
if k in hook_names:
|
|
l = hooks.get(k, [])
|
|
l.append((self, getattr(self, k)))
|
|
hooks[k] = l
|
|
|
|
def diagnostic_name(self):
|
|
return self.name
|
|
|
|
def close(self):
|
|
# remove self from hooks
|
|
for k in dir(self):
|
|
if k in hook_names:
|
|
l = hooks.get(k, [])
|
|
l.remove((self, getattr(self, k)))
|
|
hooks[k] = l
|
|
self.parent.close_plugin(self)
|
|
self.on_close()
|
|
|
|
def on_close(self):
|
|
pass
|
|
|
|
def requires_settings(self):
|
|
return False
|
|
|
|
def thread_jobs(self):
|
|
return []
|
|
|
|
def is_enabled(self):
|
|
return self.is_available() and self.config.get('use_'+self.name) is True
|
|
|
|
def is_available(self):
|
|
return True
|
|
|
|
def settings_dialog(self):
|
|
pass
|