mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-23 17:47:31 +00:00
finish separation between plugins and GUIs
This commit is contained in:
parent
2c0489c809
commit
ba2c737a21
36 changed files with 997 additions and 966 deletions
|
@ -2867,7 +2867,7 @@ class ElectrumWindow(QMainWindow, PrintError):
|
|||
run_hook('init_qt', self.gui_object)
|
||||
|
||||
for i, descr in enumerate(plugins.descriptions):
|
||||
name = descr['name']
|
||||
name = descr['__name__']
|
||||
p = plugins.get(name)
|
||||
if descr.get('registers_wallet_type'):
|
||||
continue
|
||||
|
|
|
@ -33,26 +33,25 @@ class Plugins(PrintError):
|
|||
if is_local:
|
||||
find = imp.find_module('plugins')
|
||||
plugins = imp.load_module('electrum_plugins', *find)
|
||||
self.pathname = find[1]
|
||||
else:
|
||||
plugins = __import__('electrum_plugins')
|
||||
self.pathname = None
|
||||
|
||||
self.pkgpath = os.path.dirname(plugins.__file__)
|
||||
self.plugins = {}
|
||||
self.network = None
|
||||
self.gui_name = gui_name
|
||||
self.descriptions = plugins.descriptions
|
||||
for item in self.descriptions:
|
||||
name = item['name']
|
||||
if gui_name not in item.get('available_for', []):
|
||||
self.descriptions = []
|
||||
for loader, name, ispkg in pkgutil.iter_modules([self.pkgpath]):
|
||||
m = loader.find_module(name).load_module(name)
|
||||
d = m.__dict__
|
||||
if gui_name not in d.get('available_for', []):
|
||||
continue
|
||||
x = item.get('registers_wallet_type')
|
||||
self.descriptions.append(d)
|
||||
x = d.get('registers_wallet_type')
|
||||
if x:
|
||||
self.register_wallet_type(config, name, x)
|
||||
if config.get('use_' + name):
|
||||
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)
|
||||
|
||||
|
@ -60,22 +59,10 @@ class Plugins(PrintError):
|
|||
return len(self.plugins)
|
||||
|
||||
def load_plugin(self, config, name):
|
||||
full_name = 'electrum_plugins.' + name
|
||||
full_name = 'electrum_plugins.' + name + '.' + self.gui_name
|
||||
try:
|
||||
if self.pathname: # local
|
||||
path = os.path.join(self.pathname, name + '.py')
|
||||
p = imp.load_source(full_name, path)
|
||||
else:
|
||||
p = __import__(full_name, fromlist=['electrum_plugins'])
|
||||
|
||||
if self.gui_name == 'qt':
|
||||
klass = p.QtPlugin
|
||||
elif self.gui_name == 'cmdline':
|
||||
klass = p.CmdlinePlugin
|
||||
else:
|
||||
return
|
||||
|
||||
plugin = klass(self, config, name)
|
||||
p = pkgutil.find_loader(full_name).load_module(full_name)
|
||||
plugin = p.Plugin(self, config, name)
|
||||
if self.network:
|
||||
self.network.add_jobs(plugin.thread_jobs())
|
||||
self.plugins[name] = plugin
|
||||
|
@ -103,7 +90,7 @@ class Plugins(PrintError):
|
|||
|
||||
def is_available(self, name, w):
|
||||
for d in self.descriptions:
|
||||
if d.get('name') == name:
|
||||
if d.get('__name__') == name:
|
||||
break
|
||||
else:
|
||||
return False
|
||||
|
|
|
@ -16,105 +16,4 @@
|
|||
# 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 electrum
|
||||
from electrum.i18n import _
|
||||
|
||||
descriptions = [
|
||||
{
|
||||
'name': 'audio_modem',
|
||||
'fullname': _('Audio MODEM'),
|
||||
'description': _('Provides support for air-gapped transaction signing.'),
|
||||
'requires': [('amodem', 'http://github.com/romanz/amodem/')],
|
||||
'available_for': ['qt'],
|
||||
},
|
||||
{
|
||||
'name': 'btchipwallet',
|
||||
'fullname': _('Ledger Wallet'),
|
||||
'description': _('Provides support for Ledger hardware wallet'),
|
||||
'requires': [('btchip', 'github.com/ledgerhq/btchip-python')],
|
||||
'requires_wallet_type': ['btchip'],
|
||||
'registers_wallet_type': ('hardware', 'btchip', _("Ledger wallet")),
|
||||
'available_for': ['qt', 'cmdline'],
|
||||
},
|
||||
{
|
||||
'name': 'cosigner_pool',
|
||||
'fullname': _('Cosigner Pool'),
|
||||
'description': ' '.join([
|
||||
_("This plugin facilitates the use of multi-signatures wallets."),
|
||||
_("It sends and receives partially signed transactions from/to your cosigner wallet."),
|
||||
_("Transactions are encrypted and stored on a remote server.")
|
||||
]),
|
||||
'requires_wallet_type': ['2of2', '2of3'],
|
||||
'available_for': ['qt'],
|
||||
},
|
||||
{
|
||||
'name': 'email_requests',
|
||||
'fullname': 'Email',
|
||||
'description': _("Send and receive payment request with an email account"),
|
||||
'available_for': ['qt'],
|
||||
},
|
||||
{
|
||||
'name': 'exchange_rate',
|
||||
'fullname': _("Exchange rates"),
|
||||
'description': _("Exchange rates and currency conversion tools."),
|
||||
'available_for': ['qt','kivy'],
|
||||
},
|
||||
{
|
||||
'name': 'greenaddress_instant',
|
||||
'fullname': 'GreenAddress instant',
|
||||
'description': _("Allows validating if your transactions have instant confirmations by GreenAddress"),
|
||||
'available_for': ['qt'],
|
||||
},
|
||||
{
|
||||
'name':'keepkey',
|
||||
'fullname': 'KeepKey',
|
||||
'description': _('Provides support for KeepKey hardware wallet'),
|
||||
'requires': [('keepkeylib','github.com/keepkey/python-keepkey')],
|
||||
'requires_wallet_type': ['keepkey'],
|
||||
'registers_wallet_type': ('hardware', 'keepkey', _("KeepKey wallet")),
|
||||
'available_for': ['qt', 'cmdline'],
|
||||
},
|
||||
{
|
||||
'name': 'labels',
|
||||
'fullname': _('LabelSync'),
|
||||
'description': '\n'.join([
|
||||
_("Synchronize your labels across multiple Electrum installs by using a remote database to save your data. Labels, transactions ids and addresses are encrypted before they are sent to the remote server."),
|
||||
_("The label sync's server software is open-source as well and can be found on github.com/maran/electrum-sync-server")
|
||||
]),
|
||||
'available_for': ['qt','kivy']
|
||||
},
|
||||
{
|
||||
'name': 'plot',
|
||||
'fullname': 'Plot History',
|
||||
'description': _("Ability to plot transaction history in graphical mode."),
|
||||
'requires': [('matplotlib', 'matplotlib')],
|
||||
'available_for': ['qt'],
|
||||
},
|
||||
{
|
||||
'name':'trezor',
|
||||
'fullname': 'Trezor Wallet',
|
||||
'description': _('Provides support for Trezor hardware wallet'),
|
||||
'requires': [('trezorlib','github.com/trezor/python-trezor')],
|
||||
'requires_wallet_type': ['trezor'],
|
||||
'registers_wallet_type': ('hardware', 'trezor', _("Trezor wallet")),
|
||||
'available_for': ['qt', 'cmdline'],
|
||||
},
|
||||
{
|
||||
'name': 'trustedcoin',
|
||||
'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': ('twofactor', '2fa', _("Wallet with two-factor authentication")),
|
||||
'available_for': ['qt', 'cmdline'],
|
||||
},
|
||||
{
|
||||
'name': 'virtualkeyboard',
|
||||
'fullname': 'Virtual Keyboard',
|
||||
'description': '%s\n%s' % (_("Add an optional virtual keyboard to the password dialog."), _("Warning: do not use this if it makes you pick a weaker password.")),
|
||||
'available_for': ['qt'],
|
||||
}
|
||||
]
|
||||
|
|
7
plugins/audio_modem/__init__.py
Normal file
7
plugins/audio_modem/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
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']
|
||||
|
|
@ -25,7 +25,7 @@ except ImportError:
|
|||
print_error('Audio MODEM is not found.')
|
||||
|
||||
|
||||
class QtPlugin(BasePlugin):
|
||||
class Plugin(BasePlugin):
|
||||
|
||||
def __init__(self, parent, config, name):
|
||||
BasePlugin.__init__(self, parent, config, name)
|
9
plugins/cosigner_pool/__init__.py
Normal file
9
plugins/cosigner_pool/__init__.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from electrum.i18n import _
|
||||
fullname = _('Cosigner Pool')
|
||||
description = ' '.join([
|
||||
_("This plugin facilitates the use of multi-signatures wallets."),
|
||||
_("It sends and receives partially signed transactions from/to your cosigner wallet."),
|
||||
_("Transactions are encrypted and stored on a remote server.")
|
||||
])
|
||||
requires_wallet_type = ['2of2', '2of3']
|
||||
available_for = ['qt']
|
|
@ -79,7 +79,7 @@ class Listener(util.DaemonThread):
|
|||
time.sleep(30)
|
||||
|
||||
|
||||
class QtPlugin(BasePlugin):
|
||||
class Plugin(BasePlugin):
|
||||
|
||||
def __init__(self, parent, config, name):
|
||||
BasePlugin.__init__(self, parent, config, name)
|
5
plugins/email_requests/__init__.py
Normal file
5
plugins/email_requests/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from electrum.i18n import _
|
||||
|
||||
fullname = _('Email')
|
||||
description = _("Send and receive payment request with an email account")
|
||||
available_for = ['qt']
|
|
@ -101,7 +101,7 @@ class Processor(threading.Thread):
|
|||
s.quit()
|
||||
|
||||
|
||||
class QtPlugin(BasePlugin):
|
||||
class Plugin(BasePlugin):
|
||||
|
||||
def fullname(self):
|
||||
return 'Email'
|
5
plugins/exchange_rate/__init__.py
Normal file
5
plugins/exchange_rate/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from electrum.i18n import _
|
||||
|
||||
fullname = _("Exchange rates")
|
||||
description = _("Exchange rates and currency conversion tools.")
|
||||
available_for = ['qt','kivy']
|
|
@ -7,7 +7,6 @@ import time
|
|||
import traceback
|
||||
import csv
|
||||
from decimal import Decimal
|
||||
from functools import partial
|
||||
|
||||
from electrum.bitcoin import COIN
|
||||
from electrum.plugins import BasePlugin, hook
|
||||
|
@ -235,7 +234,7 @@ class Winkdex(ExchangeBase):
|
|||
for h in history])
|
||||
|
||||
|
||||
class Plugin(BasePlugin, ThreadJob):
|
||||
class FxPlugin(BasePlugin, ThreadJob):
|
||||
|
||||
def __init__(self, parent, config, name):
|
||||
BasePlugin.__init__(self, parent, config, name)
|
||||
|
@ -243,7 +242,6 @@ class Plugin(BasePlugin, ThreadJob):
|
|||
self.history_used_spot = False
|
||||
self.ccy_combo = None
|
||||
self.hist_checkbox = None
|
||||
self.app = None
|
||||
is_exchange = lambda obj: (inspect.isclass(obj)
|
||||
and issubclass(obj, ExchangeBase)
|
||||
and obj != ExchangeBase)
|
||||
|
@ -286,16 +284,18 @@ class Plugin(BasePlugin, ThreadJob):
|
|||
if self.config_exchange() != name:
|
||||
self.config.set_key('use_exchange', name, True)
|
||||
|
||||
on_quotes = lambda: self.app.emit(SIGNAL('new_fx_quotes'))
|
||||
on_history = lambda: self.app.emit(SIGNAL('new_fx_history'))
|
||||
self.exchange = class_(on_quotes, on_history)
|
||||
self.exchange = class_(self.on_quotes, self.on_history)
|
||||
# A new exchange means new fx quotes, initially empty. Force
|
||||
# a quote refresh
|
||||
self.timeout = 0
|
||||
self.get_historical_rates()
|
||||
#self.on_fx_quotes()
|
||||
|
||||
def on_quotes(self):
|
||||
pass
|
||||
|
||||
def on_history(self):
|
||||
pass
|
||||
|
||||
def exchange_rate(self):
|
||||
'''Returns None, or the exchange rate as a Decimal'''
|
||||
|
@ -363,175 +363,3 @@ class Plugin(BasePlugin, ThreadJob):
|
|||
|
||||
|
||||
|
||||
from PyQt4.QtGui import *
|
||||
from PyQt4.QtCore import *
|
||||
from electrum_gui.qt.util import *
|
||||
from electrum_gui.qt.amountedit import AmountEdit
|
||||
|
||||
|
||||
class QtPlugin(Plugin):
|
||||
|
||||
|
||||
def connect_fields(self, window, btc_e, fiat_e, fee_e):
|
||||
|
||||
def edit_changed(edit):
|
||||
edit.setStyleSheet(BLACK_FG)
|
||||
fiat_e.is_last_edited = (edit == fiat_e)
|
||||
amount = edit.get_amount()
|
||||
rate = self.exchange_rate()
|
||||
if rate is None or amount is None:
|
||||
if edit is fiat_e:
|
||||
btc_e.setText("")
|
||||
if fee_e:
|
||||
fee_e.setText("")
|
||||
else:
|
||||
fiat_e.setText("")
|
||||
else:
|
||||
if edit is fiat_e:
|
||||
btc_e.setAmount(int(amount / Decimal(rate) * COIN))
|
||||
if fee_e: window.update_fee()
|
||||
btc_e.setStyleSheet(BLUE_FG)
|
||||
else:
|
||||
fiat_e.setText(self.ccy_amount_str(
|
||||
amount * Decimal(rate) / COIN, False))
|
||||
fiat_e.setStyleSheet(BLUE_FG)
|
||||
|
||||
fiat_e.textEdited.connect(partial(edit_changed, fiat_e))
|
||||
btc_e.textEdited.connect(partial(edit_changed, btc_e))
|
||||
fiat_e.is_last_edited = False
|
||||
|
||||
@hook
|
||||
def init_qt(self, gui):
|
||||
self.app = gui.app
|
||||
|
||||
@hook
|
||||
def do_clear(self, window):
|
||||
window.fiat_send_e.setText('')
|
||||
|
||||
def close(self):
|
||||
# Get rid of hooks before updating status bars.
|
||||
BasePlugin.close(self)
|
||||
self.app.emit(SIGNAL('close_fx_plugin'))
|
||||
|
||||
def restore_window(self, window):
|
||||
window.update_status()
|
||||
window.history_list.refresh_headers()
|
||||
window.fiat_send_e.hide()
|
||||
window.fiat_receive_e.hide()
|
||||
|
||||
def on_fx_history(self, window):
|
||||
'''Called when historical fx quotes are updated'''
|
||||
window.history_list.update()
|
||||
|
||||
def on_fx_quotes(self, window):
|
||||
'''Called when fresh spot fx quotes come in'''
|
||||
window.update_status()
|
||||
self.populate_ccy_combo()
|
||||
# Refresh edits with the new rate
|
||||
edit = window.fiat_send_e if window.fiat_send_e.is_last_edited else window.amount_e
|
||||
edit.textEdited.emit(edit.text())
|
||||
edit = window.fiat_receive_e if window.fiat_receive_e.is_last_edited else window.receive_amount_e
|
||||
edit.textEdited.emit(edit.text())
|
||||
# History tab needs updating if it used spot
|
||||
if self.history_used_spot:
|
||||
self.on_fx_history(window)
|
||||
|
||||
def on_ccy_combo_change(self):
|
||||
'''Called when the chosen currency changes'''
|
||||
ccy = str(self.ccy_combo.currentText())
|
||||
if ccy and ccy != self.ccy:
|
||||
self.ccy = ccy
|
||||
self.config.set_key('currency', ccy, True)
|
||||
self.app.emit(SIGNAL('new_fx_quotes'))
|
||||
self.get_historical_rates() # Because self.ccy changes
|
||||
self.hist_checkbox_update()
|
||||
|
||||
def hist_checkbox_update(self):
|
||||
if self.hist_checkbox:
|
||||
self.hist_checkbox.setEnabled(self.ccy in self.exchange.history_ccys())
|
||||
self.hist_checkbox.setChecked(self.config_history())
|
||||
|
||||
def populate_ccy_combo(self):
|
||||
# There should be at most one instance of the settings dialog
|
||||
combo = self.ccy_combo
|
||||
# NOTE: bool(combo) is False if it is empty. Nuts.
|
||||
if combo is not None:
|
||||
combo.blockSignals(True)
|
||||
combo.clear()
|
||||
combo.addItems(sorted(self.exchange.quotes.keys()))
|
||||
combo.blockSignals(False)
|
||||
combo.setCurrentIndex(combo.findText(self.ccy))
|
||||
|
||||
@hook
|
||||
def on_new_window(self, window):
|
||||
# Additional send and receive edit boxes
|
||||
send_e = AmountEdit(self.config_ccy)
|
||||
window.send_grid.addWidget(send_e, 4, 2, Qt.AlignLeft)
|
||||
window.amount_e.frozen.connect(
|
||||
lambda: send_e.setFrozen(window.amount_e.isReadOnly()))
|
||||
receive_e = AmountEdit(self.config_ccy)
|
||||
window.receive_grid.addWidget(receive_e, 2, 2, Qt.AlignLeft)
|
||||
window.fiat_send_e = send_e
|
||||
window.fiat_receive_e = receive_e
|
||||
self.connect_fields(window, window.amount_e, send_e, window.fee_e)
|
||||
self.connect_fields(window, window.receive_amount_e, receive_e, None)
|
||||
window.history_list.refresh_headers()
|
||||
window.update_status()
|
||||
window.connect(window.app, SIGNAL('new_fx_quotes'), lambda: self.on_fx_quotes(window))
|
||||
window.connect(window.app, SIGNAL('new_fx_history'), lambda: self.on_fx_history(window))
|
||||
window.connect(window.app, SIGNAL('close_fx_plugin'), lambda: self.restore_window(window))
|
||||
window.connect(window.app, SIGNAL('refresh_headers'), window.history_list.refresh_headers)
|
||||
|
||||
def settings_widget(self, window):
|
||||
return EnterButton(_('Settings'), self.settings_dialog)
|
||||
|
||||
def settings_dialog(self):
|
||||
d = QDialog()
|
||||
d.setWindowTitle("Settings")
|
||||
layout = QGridLayout(d)
|
||||
layout.addWidget(QLabel(_('Exchange rate API: ')), 0, 0)
|
||||
layout.addWidget(QLabel(_('Currency: ')), 1, 0)
|
||||
layout.addWidget(QLabel(_('History Rates: ')), 2, 0)
|
||||
|
||||
# Currency list
|
||||
self.ccy_combo = QComboBox()
|
||||
self.ccy_combo.currentIndexChanged.connect(self.on_ccy_combo_change)
|
||||
self.populate_ccy_combo()
|
||||
|
||||
def on_change_ex(idx):
|
||||
exchange = str(combo_ex.currentText())
|
||||
if exchange != self.exchange.name():
|
||||
self.set_exchange(exchange)
|
||||
self.hist_checkbox_update()
|
||||
|
||||
def on_change_hist(checked):
|
||||
if checked:
|
||||
self.config.set_key('history_rates', 'checked')
|
||||
self.get_historical_rates()
|
||||
else:
|
||||
self.config.set_key('history_rates', 'unchecked')
|
||||
self.app.emit(SIGNAL('refresh_headers'))
|
||||
|
||||
def ok_clicked():
|
||||
self.timeout = 0
|
||||
self.ccy_combo = None
|
||||
d.accept()
|
||||
|
||||
combo_ex = QComboBox()
|
||||
combo_ex.addItems(sorted(self.exchanges.keys()))
|
||||
combo_ex.setCurrentIndex(combo_ex.findText(self.config_exchange()))
|
||||
combo_ex.currentIndexChanged.connect(on_change_ex)
|
||||
|
||||
self.hist_checkbox = QCheckBox()
|
||||
self.hist_checkbox.stateChanged.connect(on_change_hist)
|
||||
self.hist_checkbox_update()
|
||||
|
||||
ok_button = QPushButton(_("OK"))
|
||||
ok_button.clicked.connect(lambda: ok_clicked())
|
||||
|
||||
layout.addWidget(self.ccy_combo,1,1)
|
||||
layout.addWidget(combo_ex,0,1)
|
||||
layout.addWidget(self.hist_checkbox,2,1)
|
||||
layout.addWidget(ok_button,3,1)
|
||||
|
||||
return d.exec_()
|
3
plugins/exchange_rate/kivy.py
Normal file
3
plugins/exchange_rate/kivy.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from exchange_rate import FxPlugin
|
||||
class Plugin(FxPlugin):
|
||||
pass
|
184
plugins/exchange_rate/qt.py
Normal file
184
plugins/exchange_rate/qt.py
Normal file
|
@ -0,0 +1,184 @@
|
|||
from PyQt4.QtGui import *
|
||||
from PyQt4.QtCore import *
|
||||
from electrum_gui.qt.util import *
|
||||
from electrum_gui.qt.amountedit import AmountEdit
|
||||
|
||||
|
||||
from electrum.bitcoin import COIN
|
||||
from electrum.i18n import _
|
||||
from decimal import Decimal
|
||||
from functools import partial
|
||||
from electrum.plugins import hook
|
||||
from exchange_rate import FxPlugin
|
||||
|
||||
class Plugin(FxPlugin):
|
||||
|
||||
def connect_fields(self, window, btc_e, fiat_e, fee_e):
|
||||
|
||||
def edit_changed(edit):
|
||||
edit.setStyleSheet(BLACK_FG)
|
||||
fiat_e.is_last_edited = (edit == fiat_e)
|
||||
amount = edit.get_amount()
|
||||
rate = self.exchange_rate()
|
||||
if rate is None or amount is None:
|
||||
if edit is fiat_e:
|
||||
btc_e.setText("")
|
||||
if fee_e:
|
||||
fee_e.setText("")
|
||||
else:
|
||||
fiat_e.setText("")
|
||||
else:
|
||||
if edit is fiat_e:
|
||||
btc_e.setAmount(int(amount / Decimal(rate) * COIN))
|
||||
if fee_e: window.update_fee()
|
||||
btc_e.setStyleSheet(BLUE_FG)
|
||||
else:
|
||||
fiat_e.setText(self.ccy_amount_str(
|
||||
amount * Decimal(rate) / COIN, False))
|
||||
fiat_e.setStyleSheet(BLUE_FG)
|
||||
|
||||
fiat_e.textEdited.connect(partial(edit_changed, fiat_e))
|
||||
btc_e.textEdited.connect(partial(edit_changed, btc_e))
|
||||
fiat_e.is_last_edited = False
|
||||
|
||||
@hook
|
||||
def init_qt(self, gui):
|
||||
self.app = gui.app
|
||||
|
||||
@hook
|
||||
def do_clear(self, window):
|
||||
window.fiat_send_e.setText('')
|
||||
|
||||
def close(self):
|
||||
# Get rid of hooks before updating status bars.
|
||||
FxPlugin.close(self)
|
||||
self.app.emit(SIGNAL('close_fx_plugin'))
|
||||
|
||||
def restore_window(self, window):
|
||||
window.update_status()
|
||||
window.history_list.refresh_headers()
|
||||
window.fiat_send_e.hide()
|
||||
window.fiat_receive_e.hide()
|
||||
|
||||
def on_quotes(self):
|
||||
self.app.emit(SIGNAL('new_fx_quotes'))
|
||||
|
||||
def on_history(self):
|
||||
self.app.emit(SIGNAL('new_fx_history'))
|
||||
|
||||
def on_fx_history(self, window):
|
||||
'''Called when historical fx quotes are updated'''
|
||||
window.history_list.update()
|
||||
|
||||
def on_fx_quotes(self, window):
|
||||
'''Called when fresh spot fx quotes come in'''
|
||||
window.update_status()
|
||||
self.populate_ccy_combo()
|
||||
# Refresh edits with the new rate
|
||||
edit = window.fiat_send_e if window.fiat_send_e.is_last_edited else window.amount_e
|
||||
edit.textEdited.emit(edit.text())
|
||||
edit = window.fiat_receive_e if window.fiat_receive_e.is_last_edited else window.receive_amount_e
|
||||
edit.textEdited.emit(edit.text())
|
||||
# History tab needs updating if it used spot
|
||||
if self.history_used_spot:
|
||||
self.on_fx_history(window)
|
||||
|
||||
def on_ccy_combo_change(self):
|
||||
'''Called when the chosen currency changes'''
|
||||
ccy = str(self.ccy_combo.currentText())
|
||||
if ccy and ccy != self.ccy:
|
||||
self.ccy = ccy
|
||||
self.config.set_key('currency', ccy, True)
|
||||
self.app.emit(SIGNAL('new_fx_quotes'))
|
||||
self.get_historical_rates() # Because self.ccy changes
|
||||
self.hist_checkbox_update()
|
||||
|
||||
def hist_checkbox_update(self):
|
||||
if self.hist_checkbox:
|
||||
self.hist_checkbox.setEnabled(self.ccy in self.exchange.history_ccys())
|
||||
self.hist_checkbox.setChecked(self.config_history())
|
||||
|
||||
def populate_ccy_combo(self):
|
||||
# There should be at most one instance of the settings dialog
|
||||
combo = self.ccy_combo
|
||||
# NOTE: bool(combo) is False if it is empty. Nuts.
|
||||
if combo is not None:
|
||||
combo.blockSignals(True)
|
||||
combo.clear()
|
||||
combo.addItems(sorted(self.exchange.quotes.keys()))
|
||||
combo.blockSignals(False)
|
||||
combo.setCurrentIndex(combo.findText(self.ccy))
|
||||
|
||||
@hook
|
||||
def on_new_window(self, window):
|
||||
# Additional send and receive edit boxes
|
||||
send_e = AmountEdit(self.config_ccy)
|
||||
window.send_grid.addWidget(send_e, 4, 2, Qt.AlignLeft)
|
||||
window.amount_e.frozen.connect(
|
||||
lambda: send_e.setFrozen(window.amount_e.isReadOnly()))
|
||||
receive_e = AmountEdit(self.config_ccy)
|
||||
window.receive_grid.addWidget(receive_e, 2, 2, Qt.AlignLeft)
|
||||
window.fiat_send_e = send_e
|
||||
window.fiat_receive_e = receive_e
|
||||
self.connect_fields(window, window.amount_e, send_e, window.fee_e)
|
||||
self.connect_fields(window, window.receive_amount_e, receive_e, None)
|
||||
window.history_list.refresh_headers()
|
||||
window.update_status()
|
||||
window.connect(window.app, SIGNAL('new_fx_quotes'), lambda: self.on_fx_quotes(window))
|
||||
window.connect(window.app, SIGNAL('new_fx_history'), lambda: self.on_fx_history(window))
|
||||
window.connect(window.app, SIGNAL('close_fx_plugin'), lambda: self.restore_window(window))
|
||||
window.connect(window.app, SIGNAL('refresh_headers'), window.history_list.refresh_headers)
|
||||
|
||||
def settings_widget(self, window):
|
||||
return EnterButton(_('Settings'), self.settings_dialog)
|
||||
|
||||
def settings_dialog(self):
|
||||
d = QDialog()
|
||||
d.setWindowTitle("Settings")
|
||||
layout = QGridLayout(d)
|
||||
layout.addWidget(QLabel(_('Exchange rate API: ')), 0, 0)
|
||||
layout.addWidget(QLabel(_('Currency: ')), 1, 0)
|
||||
layout.addWidget(QLabel(_('History Rates: ')), 2, 0)
|
||||
|
||||
# Currency list
|
||||
self.ccy_combo = QComboBox()
|
||||
self.ccy_combo.currentIndexChanged.connect(self.on_ccy_combo_change)
|
||||
self.populate_ccy_combo()
|
||||
|
||||
def on_change_ex(idx):
|
||||
exchange = str(combo_ex.currentText())
|
||||
if exchange != self.exchange.name():
|
||||
self.set_exchange(exchange)
|
||||
self.hist_checkbox_update()
|
||||
|
||||
def on_change_hist(checked):
|
||||
if checked:
|
||||
self.config.set_key('history_rates', 'checked')
|
||||
self.get_historical_rates()
|
||||
else:
|
||||
self.config.set_key('history_rates', 'unchecked')
|
||||
self.app.emit(SIGNAL('refresh_headers'))
|
||||
|
||||
def ok_clicked():
|
||||
self.timeout = 0
|
||||
self.ccy_combo = None
|
||||
d.accept()
|
||||
|
||||
combo_ex = QComboBox()
|
||||
combo_ex.addItems(sorted(self.exchanges.keys()))
|
||||
combo_ex.setCurrentIndex(combo_ex.findText(self.config_exchange()))
|
||||
combo_ex.currentIndexChanged.connect(on_change_ex)
|
||||
|
||||
self.hist_checkbox = QCheckBox()
|
||||
self.hist_checkbox.stateChanged.connect(on_change_hist)
|
||||
self.hist_checkbox_update()
|
||||
|
||||
ok_button = QPushButton(_("OK"))
|
||||
ok_button.clicked.connect(lambda: ok_clicked())
|
||||
|
||||
layout.addWidget(self.ccy_combo,1,1)
|
||||
layout.addWidget(combo_ex,0,1)
|
||||
layout.addWidget(self.hist_checkbox,2,1)
|
||||
layout.addWidget(ok_button,3,1)
|
||||
|
||||
return d.exec_()
|
5
plugins/greenaddress_instant/__init__.py
Normal file
5
plugins/greenaddress_instant/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from electrum.i18n import _
|
||||
|
||||
fullname = 'GreenAddress instant'
|
||||
description = _("Allows validating if your transactions have instant confirmations by GreenAddress")
|
||||
available_for = ['qt']
|
|
@ -28,7 +28,7 @@ from electrum.i18n import _
|
|||
|
||||
|
||||
|
||||
class QtPlugin(BasePlugin):
|
||||
class Plugin(BasePlugin):
|
||||
|
||||
button_label = _("Verify GA instant")
|
||||
|
8
plugins/keepkey/__init__.py
Normal file
8
plugins/keepkey/__init__.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from electrum.i18n import _
|
||||
|
||||
fullname = 'KeepKey'
|
||||
description = _('Provides support for KeepKey hardware wallet')
|
||||
requires = [('keepkeylib','github.com/keepkey/python-keepkey')]
|
||||
requires_wallet_type = ['keepkey']
|
||||
registers_wallet_type = ('hardware', 'keepkey', _("KeepKey wallet"))
|
||||
available_for = ['qt', 'cmdline']
|
|
@ -207,7 +207,7 @@ class KeepKeyWallet(BIP32_HD_Wallet):
|
|||
|
||||
|
||||
|
||||
class Plugin(BasePlugin):
|
||||
class KeepKeyPlugin(BasePlugin):
|
||||
|
||||
def __init__(self, parent, config, name):
|
||||
BasePlugin.__init__(self, parent, config, name)
|
||||
|
@ -419,100 +419,6 @@ class Plugin(BasePlugin):
|
|||
|
||||
|
||||
|
||||
from PyQt4.Qt import QMessageBox, QDialog, QVBoxLayout, QLabel, QThread, SIGNAL, QGridLayout, QInputDialog, QPushButton
|
||||
import PyQt4.QtCore as QtCore
|
||||
from electrum_gui.qt.util import *
|
||||
from electrum_gui.qt.main_window import StatusBarButton, ElectrumWindow
|
||||
from electrum_gui.qt.installwizard import InstallWizard
|
||||
from keepkeylib.qt.pinmatrix import PinMatrixWidget
|
||||
|
||||
|
||||
class QtPlugin(Plugin):
|
||||
|
||||
@hook
|
||||
def load_wallet(self, wallet, window):
|
||||
self.print_error("load_wallet")
|
||||
self.wallet = wallet
|
||||
self.wallet.plugin = self
|
||||
self.keepkey_button = StatusBarButton(QIcon(":icons/keepkey.png"), _("KeepKey"), partial(self.settings_dialog, window))
|
||||
if type(window) is ElectrumWindow:
|
||||
window.statusBar().addPermanentWidget(self.keepkey_button)
|
||||
if self.handler is None:
|
||||
self.handler = KeepKeyQtHandler(window)
|
||||
try:
|
||||
self.get_client().ping('t')
|
||||
except BaseException as e:
|
||||
QMessageBox.information(window, _('Error'), _("KeepKey device not detected.\nContinuing in watching-only mode." + '\n\nReason:\n' + str(e)), _('OK'))
|
||||
self.wallet.force_watching_only = True
|
||||
return
|
||||
if self.wallet.addresses() and not self.wallet.check_proper_device():
|
||||
QMessageBox.information(window, _('Error'), _("This wallet does not match your KeepKey device"), _('OK'))
|
||||
self.wallet.force_watching_only = True
|
||||
|
||||
@hook
|
||||
def installwizard_load_wallet(self, wallet, window):
|
||||
if type(wallet) != KeepKeyWallet:
|
||||
return
|
||||
self.load_wallet(wallet, window)
|
||||
|
||||
@hook
|
||||
def installwizard_restore(self, wizard, storage):
|
||||
if storage.get('wallet_type') != 'keepkey':
|
||||
return
|
||||
seed = wizard.enter_seed_dialog("Enter your KeepKey seed", None, func=lambda x:True)
|
||||
if not seed:
|
||||
return
|
||||
wallet = KeepKeyWallet(storage)
|
||||
self.wallet = wallet
|
||||
handler = KeepKeyQtHandler(wizard)
|
||||
passphrase = handler.get_passphrase(_("Please enter your KeepKey passphrase.") + '\n' + _("Press OK if you do not use one."))
|
||||
if passphrase is None:
|
||||
return
|
||||
password = wizard.password_dialog()
|
||||
wallet.add_seed(seed, password)
|
||||
wallet.add_cosigner_seed(seed, 'x/', password, passphrase)
|
||||
wallet.create_main_account(password)
|
||||
# disable keepkey plugin
|
||||
self.set_enabled(False)
|
||||
return wallet
|
||||
|
||||
@hook
|
||||
def receive_menu(self, menu, addrs):
|
||||
if not self.wallet.is_watching_only() and self.atleast_version(1, 3) and len(addrs) == 1:
|
||||
menu.addAction(_("Show on TREZOR"), lambda: self.show_address(addrs[0]))
|
||||
|
||||
def settings_dialog(self, window):
|
||||
try:
|
||||
device_id = self.get_client().get_device_id()
|
||||
except BaseException as e:
|
||||
window.show_message(str(e))
|
||||
return
|
||||
get_label = lambda: self.get_client().features.label
|
||||
update_label = lambda: current_label_label.setText("Label: %s" % get_label())
|
||||
d = QDialog()
|
||||
layout = QGridLayout(d)
|
||||
layout.addWidget(QLabel("KeepKey Options"),0,0)
|
||||
layout.addWidget(QLabel("ID:"),1,0)
|
||||
layout.addWidget(QLabel(" %s" % device_id),1,1)
|
||||
|
||||
def modify_label():
|
||||
response = QInputDialog().getText(None, "Set New KeepKey Label", "New KeepKey Label: (upon submission confirm on KeepKey)")
|
||||
if not response[1]:
|
||||
return
|
||||
new_label = str(response[0])
|
||||
self.handler.show_message("Please confirm label change on KeepKey")
|
||||
status = self.get_client().apply_settings(label=new_label)
|
||||
self.handler.stop()
|
||||
update_label()
|
||||
|
||||
current_label_label = QLabel()
|
||||
update_label()
|
||||
change_label_button = QPushButton("Modify")
|
||||
change_label_button.clicked.connect(modify_label)
|
||||
layout.addWidget(current_label_label,3,0)
|
||||
layout.addWidget(change_label_button,3,1)
|
||||
d.exec_()
|
||||
|
||||
|
||||
class CmdlinePlugin(Plugin):
|
||||
|
95
plugins/keepkey/qt.py
Normal file
95
plugins/keepkey/qt.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
from PyQt4.Qt import QMessageBox, QDialog, QVBoxLayout, QLabel, QThread, SIGNAL, QGridLayout, QInputDialog, QPushButton
|
||||
import PyQt4.QtCore as QtCore
|
||||
from electrum_gui.qt.util import *
|
||||
from electrum_gui.qt.main_window import StatusBarButton, ElectrumWindow
|
||||
from electrum_gui.qt.installwizard import InstallWizard
|
||||
from keepkeylib.qt.pinmatrix import PinMatrixWidget
|
||||
|
||||
from keepkey import KeepKeyPlugin
|
||||
|
||||
class Plugin(KeepKeyPlugin):
|
||||
|
||||
@hook
|
||||
def load_wallet(self, wallet, window):
|
||||
self.print_error("load_wallet")
|
||||
self.wallet = wallet
|
||||
self.wallet.plugin = self
|
||||
self.keepkey_button = StatusBarButton(QIcon(":icons/keepkey.png"), _("KeepKey"), partial(self.settings_dialog, window))
|
||||
if type(window) is ElectrumWindow:
|
||||
window.statusBar().addPermanentWidget(self.keepkey_button)
|
||||
if self.handler is None:
|
||||
self.handler = KeepKeyQtHandler(window)
|
||||
try:
|
||||
self.get_client().ping('t')
|
||||
except BaseException as e:
|
||||
QMessageBox.information(window, _('Error'), _("KeepKey device not detected.\nContinuing in watching-only mode." + '\n\nReason:\n' + str(e)), _('OK'))
|
||||
self.wallet.force_watching_only = True
|
||||
return
|
||||
if self.wallet.addresses() and not self.wallet.check_proper_device():
|
||||
QMessageBox.information(window, _('Error'), _("This wallet does not match your KeepKey device"), _('OK'))
|
||||
self.wallet.force_watching_only = True
|
||||
|
||||
@hook
|
||||
def installwizard_load_wallet(self, wallet, window):
|
||||
if type(wallet) != KeepKeyWallet:
|
||||
return
|
||||
self.load_wallet(wallet, window)
|
||||
|
||||
@hook
|
||||
def installwizard_restore(self, wizard, storage):
|
||||
if storage.get('wallet_type') != 'keepkey':
|
||||
return
|
||||
seed = wizard.enter_seed_dialog("Enter your KeepKey seed", None, func=lambda x:True)
|
||||
if not seed:
|
||||
return
|
||||
wallet = KeepKeyWallet(storage)
|
||||
self.wallet = wallet
|
||||
handler = KeepKeyQtHandler(wizard)
|
||||
passphrase = handler.get_passphrase(_("Please enter your KeepKey passphrase.") + '\n' + _("Press OK if you do not use one."))
|
||||
if passphrase is None:
|
||||
return
|
||||
password = wizard.password_dialog()
|
||||
wallet.add_seed(seed, password)
|
||||
wallet.add_cosigner_seed(seed, 'x/', password, passphrase)
|
||||
wallet.create_main_account(password)
|
||||
# disable keepkey plugin
|
||||
self.set_enabled(False)
|
||||
return wallet
|
||||
|
||||
@hook
|
||||
def receive_menu(self, menu, addrs):
|
||||
if not self.wallet.is_watching_only() and self.atleast_version(1, 3) and len(addrs) == 1:
|
||||
menu.addAction(_("Show on TREZOR"), lambda: self.show_address(addrs[0]))
|
||||
|
||||
def settings_dialog(self, window):
|
||||
try:
|
||||
device_id = self.get_client().get_device_id()
|
||||
except BaseException as e:
|
||||
window.show_message(str(e))
|
||||
return
|
||||
get_label = lambda: self.get_client().features.label
|
||||
update_label = lambda: current_label_label.setText("Label: %s" % get_label())
|
||||
d = QDialog()
|
||||
layout = QGridLayout(d)
|
||||
layout.addWidget(QLabel("KeepKey Options"),0,0)
|
||||
layout.addWidget(QLabel("ID:"),1,0)
|
||||
layout.addWidget(QLabel(" %s" % device_id),1,1)
|
||||
|
||||
def modify_label():
|
||||
response = QInputDialog().getText(None, "Set New KeepKey Label", "New KeepKey Label: (upon submission confirm on KeepKey)")
|
||||
if not response[1]:
|
||||
return
|
||||
new_label = str(response[0])
|
||||
self.handler.show_message("Please confirm label change on KeepKey")
|
||||
status = self.get_client().apply_settings(label=new_label)
|
||||
self.handler.stop()
|
||||
update_label()
|
||||
|
||||
current_label_label = QLabel()
|
||||
update_label()
|
||||
change_label_button = QPushButton("Modify")
|
||||
change_label_button.clicked.connect(modify_label)
|
||||
layout.addWidget(current_label_label,3,0)
|
||||
layout.addWidget(change_label_button,3,1)
|
||||
d.exec_()
|
||||
|
9
plugins/labels/__init__.py
Normal file
9
plugins/labels/__init__.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from electrum.i18n import _
|
||||
|
||||
fullname = _('LabelSync')
|
||||
description = '\n'.join([
|
||||
_("Synchronize your labels across multiple Electrum installs by using a remote database to save your data. Labels, transactions ids and addresses are encrypted before they are sent to the remote server."),
|
||||
_("The label sync's server software is open-source as well and can be found on github.com/maran/electrum-sync-server")
|
||||
])
|
||||
available_for = ['qt', 'kivy']
|
||||
|
3
plugins/labels/kivy.py
Normal file
3
plugins/labels/kivy.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from labels import LabelsPlugin
|
||||
class Plugin(LabelsPlugin):
|
||||
pass
|
|
@ -1,11 +1,8 @@
|
|||
import socket
|
||||
import requests
|
||||
import threading
|
||||
import hashlib
|
||||
import json
|
||||
import sys
|
||||
import traceback
|
||||
from functools import partial
|
||||
|
||||
import aes
|
||||
import base64
|
||||
|
@ -15,7 +12,9 @@ from electrum.plugins import BasePlugin, hook
|
|||
from electrum.i18n import _
|
||||
|
||||
|
||||
class Plugin(BasePlugin):
|
||||
|
||||
|
||||
class LabelsPlugin(BasePlugin):
|
||||
|
||||
def __init__(self, parent, config, name):
|
||||
BasePlugin.__init__(self, parent, config, name)
|
||||
|
@ -137,73 +136,3 @@ class Plugin(BasePlugin):
|
|||
|
||||
|
||||
|
||||
from PyQt4.QtGui import *
|
||||
from PyQt4.QtCore import *
|
||||
import PyQt4.QtCore as QtCore
|
||||
import PyQt4.QtGui as QtGui
|
||||
from electrum_gui.qt import HelpButton, EnterButton
|
||||
from electrum_gui.qt.util import ThreadedButton, Buttons, CancelButton, OkButton
|
||||
|
||||
class QtPlugin(Plugin):
|
||||
|
||||
def __init__(self, *args):
|
||||
Plugin.__init__(self, *args)
|
||||
self.obj = QObject()
|
||||
|
||||
def requires_settings(self):
|
||||
return True
|
||||
|
||||
def settings_widget(self, window):
|
||||
return EnterButton(_('Settings'),
|
||||
partial(self.settings_dialog, window))
|
||||
|
||||
def settings_dialog(self, window):
|
||||
d = QDialog(window)
|
||||
vbox = QVBoxLayout(d)
|
||||
layout = QGridLayout()
|
||||
vbox.addLayout(layout)
|
||||
layout.addWidget(QLabel("Label sync options: "), 2, 0)
|
||||
self.upload = ThreadedButton("Force upload",
|
||||
partial(self.push_thread, window.wallet),
|
||||
self.done_processing)
|
||||
layout.addWidget(self.upload, 2, 1)
|
||||
self.download = ThreadedButton("Force download",
|
||||
partial(self.pull_thread, window.wallet, True),
|
||||
self.done_processing)
|
||||
layout.addWidget(self.download, 2, 2)
|
||||
self.accept = OkButton(d, _("Done"))
|
||||
vbox.addLayout(Buttons(CancelButton(d), self.accept))
|
||||
if d.exec_():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def on_pulled(self, wallet):
|
||||
self.obj.emit(SIGNAL('labels_changed'), wallet)
|
||||
|
||||
def done_processing(self):
|
||||
QMessageBox.information(None, _("Labels synchronised"),
|
||||
_("Your labels have been synchronised."))
|
||||
|
||||
@hook
|
||||
def on_new_window(self, window):
|
||||
window.connect(window.app, SIGNAL('labels_changed'), window.update_tabs)
|
||||
wallet = window.wallet
|
||||
nonce = self.get_nonce(wallet)
|
||||
self.print_error("wallet", wallet.basename(), "nonce is", nonce)
|
||||
mpk = ''.join(sorted(wallet.get_master_public_keys().values()))
|
||||
if not mpk:
|
||||
return
|
||||
password = hashlib.sha1(mpk).digest().encode('hex')[:32]
|
||||
iv = hashlib.sha256(password).digest()[:16]
|
||||
wallet_id = hashlib.sha256(mpk).digest().encode('hex')
|
||||
self.wallets[wallet] = (password, iv, wallet_id)
|
||||
# If there is an auth token we can try to actually start syncing
|
||||
t = threading.Thread(target=self.pull_thread, args=(wallet, False))
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
||||
@hook
|
||||
def on_close_window(self, window):
|
||||
self.wallets.pop(window.wallet)
|
||||
|
80
plugins/labels/qt.py
Normal file
80
plugins/labels/qt.py
Normal file
|
@ -0,0 +1,80 @@
|
|||
import hashlib
|
||||
import threading
|
||||
from functools import partial
|
||||
|
||||
from PyQt4.QtGui import *
|
||||
from PyQt4.QtCore import *
|
||||
import PyQt4.QtCore as QtCore
|
||||
import PyQt4.QtGui as QtGui
|
||||
|
||||
from electrum.plugins import hook
|
||||
from electrum.i18n import _
|
||||
from electrum_gui.qt import HelpButton, EnterButton
|
||||
from electrum_gui.qt.util import ThreadedButton, Buttons, CancelButton, OkButton
|
||||
|
||||
from labels import LabelsPlugin
|
||||
|
||||
|
||||
class Plugin(LabelsPlugin):
|
||||
|
||||
def __init__(self, *args):
|
||||
LabelsPlugin.__init__(self, *args)
|
||||
self.obj = QObject()
|
||||
|
||||
def requires_settings(self):
|
||||
return True
|
||||
|
||||
def settings_widget(self, window):
|
||||
return EnterButton(_('Settings'),
|
||||
partial(self.settings_dialog, window))
|
||||
|
||||
def settings_dialog(self, window):
|
||||
d = QDialog(window)
|
||||
vbox = QVBoxLayout(d)
|
||||
layout = QGridLayout()
|
||||
vbox.addLayout(layout)
|
||||
layout.addWidget(QLabel("Label sync options: "), 2, 0)
|
||||
self.upload = ThreadedButton("Force upload",
|
||||
partial(self.push_thread, window.wallet),
|
||||
self.done_processing)
|
||||
layout.addWidget(self.upload, 2, 1)
|
||||
self.download = ThreadedButton("Force download",
|
||||
partial(self.pull_thread, window.wallet, True),
|
||||
self.done_processing)
|
||||
layout.addWidget(self.download, 2, 2)
|
||||
self.accept = OkButton(d, _("Done"))
|
||||
vbox.addLayout(Buttons(CancelButton(d), self.accept))
|
||||
if d.exec_():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def on_pulled(self, wallet):
|
||||
self.obj.emit(SIGNAL('labels_changed'), wallet)
|
||||
|
||||
def done_processing(self):
|
||||
QMessageBox.information(None, _("Labels synchronised"),
|
||||
_("Your labels have been synchronised."))
|
||||
|
||||
@hook
|
||||
def on_new_window(self, window):
|
||||
window.connect(window.app, SIGNAL('labels_changed'), window.update_tabs)
|
||||
wallet = window.wallet
|
||||
nonce = self.get_nonce(wallet)
|
||||
self.print_error("wallet", wallet.basename(), "nonce is", nonce)
|
||||
mpk = ''.join(sorted(wallet.get_master_public_keys().values()))
|
||||
if not mpk:
|
||||
return
|
||||
password = hashlib.sha1(mpk).digest().encode('hex')[:32]
|
||||
iv = hashlib.sha256(password).digest()[:16]
|
||||
wallet_id = hashlib.sha256(mpk).digest().encode('hex')
|
||||
self.wallets[wallet] = (password, iv, wallet_id)
|
||||
# If there is an auth token we can try to actually start syncing
|
||||
t = threading.Thread(target=self.pull_thread, args=(wallet, False))
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
||||
@hook
|
||||
def on_close_window(self, window):
|
||||
self.wallets.pop(window.wallet)
|
||||
|
8
plugins/ledger/__init__.py
Normal file
8
plugins/ledger/__init__.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from electrum.i18n import _
|
||||
|
||||
fullname = 'Ledger Wallet'
|
||||
description = 'Provides support for Ledger hardware wallet'
|
||||
requires = [('btchip', 'github.com/ledgerhq/btchip-python')]
|
||||
requires_wallet_type = ['btchip']
|
||||
registers_wallet_type = ('hardware', 'btchip', _("Ledger wallet"))
|
||||
available_for = ['qt', 'cmdline']
|
|
@ -425,7 +425,7 @@ class BTChipWallet(BIP32_HD_Wallet):
|
|||
return True, response, response
|
||||
|
||||
|
||||
class Plugin(BasePlugin):
|
||||
class LedgerPlugin(BasePlugin):
|
||||
|
||||
def __init__(self, parent, config, name):
|
||||
BasePlugin.__init__(self, parent, config, name)
|
||||
|
@ -495,72 +495,6 @@ class Plugin(BasePlugin):
|
|||
except Exception as e:
|
||||
tx.error = str(e)
|
||||
|
||||
from PyQt4.Qt import QApplication, QMessageBox, QDialog, QInputDialog, QLineEdit, QVBoxLayout, QLabel, QThread, SIGNAL
|
||||
import PyQt4.QtCore as QtCore
|
||||
from electrum_gui.qt.password_dialog import make_password_dialog, run_password_dialog
|
||||
|
||||
class QtPlugin(Plugin):
|
||||
|
||||
@hook
|
||||
def load_wallet(self, wallet, window):
|
||||
self.wallet = wallet
|
||||
self.wallet.plugin = self
|
||||
if self.handler is None:
|
||||
self.handler = BTChipQTHandler(window)
|
||||
if self.btchip_is_connected():
|
||||
if not self.wallet.check_proper_device():
|
||||
QMessageBox.information(window, _('Error'), _("This wallet does not match your Ledger device"), _('OK'))
|
||||
self.wallet.force_watching_only = True
|
||||
else:
|
||||
QMessageBox.information(window, _('Error'), _("Ledger device not detected.\nContinuing in watching-only mode."), _('OK'))
|
||||
self.wallet.force_watching_only = True
|
||||
|
||||
|
||||
class BTChipQTHandler:
|
||||
|
||||
def __init__(self, win):
|
||||
self.win = win
|
||||
self.win.connect(win, SIGNAL('btchip_done'), self.dialog_stop)
|
||||
self.win.connect(win, SIGNAL('btchip_message_dialog'), self.message_dialog)
|
||||
self.win.connect(win, SIGNAL('btchip_auth_dialog'), self.auth_dialog)
|
||||
self.done = threading.Event()
|
||||
|
||||
def stop(self):
|
||||
self.win.emit(SIGNAL('btchip_done'))
|
||||
|
||||
def show_message(self, msg):
|
||||
self.message = msg
|
||||
self.win.emit(SIGNAL('btchip_message_dialog'))
|
||||
|
||||
def prompt_auth(self, msg):
|
||||
self.done.clear()
|
||||
self.message = msg
|
||||
self.win.emit(SIGNAL('btchip_auth_dialog'))
|
||||
self.done.wait()
|
||||
return self.response
|
||||
|
||||
def auth_dialog(self):
|
||||
response = QInputDialog.getText(None, "Ledger Wallet Authentication", self.message, QLineEdit.Password)
|
||||
if not response[1]:
|
||||
self.response = None
|
||||
else:
|
||||
self.response = str(response[0])
|
||||
self.done.set()
|
||||
|
||||
def message_dialog(self):
|
||||
self.d = QDialog()
|
||||
self.d.setModal(1)
|
||||
self.d.setWindowTitle('Ledger')
|
||||
self.d.setWindowFlags(self.d.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
|
||||
l = QLabel(self.message)
|
||||
vbox = QVBoxLayout(self.d)
|
||||
vbox.addWidget(l)
|
||||
self.d.show()
|
||||
|
||||
def dialog_stop(self):
|
||||
if self.d is not None:
|
||||
self.d.hide()
|
||||
self.d = None
|
||||
|
||||
class CmdlinePlugin(Plugin):
|
||||
@hook
|
66
plugins/ledger/qt.py
Normal file
66
plugins/ledger/qt.py
Normal file
|
@ -0,0 +1,66 @@
|
|||
from PyQt4.Qt import QApplication, QMessageBox, QDialog, QInputDialog, QLineEdit, QVBoxLayout, QLabel, QThread, SIGNAL
|
||||
import PyQt4.QtCore as QtCore
|
||||
from electrum_gui.qt.password_dialog import make_password_dialog, run_password_dialog
|
||||
|
||||
class Plugin(LedgerPlugin):
|
||||
|
||||
@hook
|
||||
def load_wallet(self, wallet, window):
|
||||
self.wallet = wallet
|
||||
self.wallet.plugin = self
|
||||
if self.handler is None:
|
||||
self.handler = BTChipQTHandler(window)
|
||||
if self.btchip_is_connected():
|
||||
if not self.wallet.check_proper_device():
|
||||
QMessageBox.information(window, _('Error'), _("This wallet does not match your Ledger device"), _('OK'))
|
||||
self.wallet.force_watching_only = True
|
||||
else:
|
||||
QMessageBox.information(window, _('Error'), _("Ledger device not detected.\nContinuing in watching-only mode."), _('OK'))
|
||||
self.wallet.force_watching_only = True
|
||||
|
||||
|
||||
class BTChipQTHandler:
|
||||
|
||||
def __init__(self, win):
|
||||
self.win = win
|
||||
self.win.connect(win, SIGNAL('btchip_done'), self.dialog_stop)
|
||||
self.win.connect(win, SIGNAL('btchip_message_dialog'), self.message_dialog)
|
||||
self.win.connect(win, SIGNAL('btchip_auth_dialog'), self.auth_dialog)
|
||||
self.done = threading.Event()
|
||||
|
||||
def stop(self):
|
||||
self.win.emit(SIGNAL('btchip_done'))
|
||||
|
||||
def show_message(self, msg):
|
||||
self.message = msg
|
||||
self.win.emit(SIGNAL('btchip_message_dialog'))
|
||||
|
||||
def prompt_auth(self, msg):
|
||||
self.done.clear()
|
||||
self.message = msg
|
||||
self.win.emit(SIGNAL('btchip_auth_dialog'))
|
||||
self.done.wait()
|
||||
return self.response
|
||||
|
||||
def auth_dialog(self):
|
||||
response = QInputDialog.getText(None, "Ledger Wallet Authentication", self.message, QLineEdit.Password)
|
||||
if not response[1]:
|
||||
self.response = None
|
||||
else:
|
||||
self.response = str(response[0])
|
||||
self.done.set()
|
||||
|
||||
def message_dialog(self):
|
||||
self.d = QDialog()
|
||||
self.d.setModal(1)
|
||||
self.d.setWindowTitle('Ledger')
|
||||
self.d.setWindowFlags(self.d.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
|
||||
l = QLabel(self.message)
|
||||
vbox = QVBoxLayout(self.d)
|
||||
vbox.addWidget(l)
|
||||
self.d.show()
|
||||
|
||||
def dialog_stop(self):
|
||||
if self.d is not None:
|
||||
self.d.hide()
|
||||
self.d = None
|
6
plugins/plot/__init__.py
Normal file
6
plugins/plot/__init__.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from electrum.i18n import _
|
||||
|
||||
fullname = 'Plot History'
|
||||
description = _("Ability to plot transaction history in graphical mode.")
|
||||
requires = [('matplotlib', 'matplotlib')]
|
||||
available_for = ['qt']
|
|
@ -17,7 +17,7 @@ except:
|
|||
flag_matlib=False
|
||||
|
||||
|
||||
class QtPlugin(BasePlugin):
|
||||
class Plugin(BasePlugin):
|
||||
|
||||
def is_available(self):
|
||||
if flag_matlib:
|
9
plugins/trezor/__init__.py
Normal file
9
plugins/trezor/__init__.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from electrum.i18n import _
|
||||
|
||||
fullname = 'Trezor Wallet'
|
||||
description = _('Provides support for Trezor hardware wallet')
|
||||
requires = [('trezorlib','github.com/trezor/python-trezor')]
|
||||
requires_wallet_type = ['trezor']
|
||||
registers_wallet_type = ('hardware', 'trezor', _("Trezor wallet"))
|
||||
available_for = ['qt', 'cmdline']
|
||||
|
200
plugins/trezor/qt.py
Normal file
200
plugins/trezor/qt.py
Normal file
|
@ -0,0 +1,200 @@
|
|||
from PyQt4.Qt import QMessageBox, QDialog, QVBoxLayout, QLabel, QThread, SIGNAL, QGridLayout, QInputDialog, QPushButton
|
||||
import PyQt4.QtCore as QtCore
|
||||
from electrum_gui.qt.util import *
|
||||
from electrum_gui.qt.main_window import StatusBarButton, ElectrumWindow
|
||||
from electrum_gui.qt.installwizard import InstallWizard
|
||||
from trezorlib.qt.pinmatrix import PinMatrixWidget
|
||||
|
||||
|
||||
from functools import partial
|
||||
import unicodedata
|
||||
|
||||
from electrum.i18n import _
|
||||
from electrum.plugins import hook, always_hook, run_hook
|
||||
|
||||
from trezor import TrezorPlugin
|
||||
|
||||
class TrezorQtHandler:
|
||||
|
||||
def __init__(self, win):
|
||||
self.win = win
|
||||
self.win.connect(win, SIGNAL('trezor_done'), self.dialog_stop)
|
||||
self.win.connect(win, SIGNAL('message_dialog'), self.message_dialog)
|
||||
self.win.connect(win, SIGNAL('pin_dialog'), self.pin_dialog)
|
||||
self.win.connect(win, SIGNAL('passphrase_dialog'), self.passphrase_dialog)
|
||||
self.done = threading.Event()
|
||||
|
||||
def stop(self):
|
||||
self.win.emit(SIGNAL('trezor_done'))
|
||||
|
||||
def show_message(self, msg):
|
||||
self.message = msg
|
||||
self.win.emit(SIGNAL('message_dialog'))
|
||||
|
||||
def get_pin(self, msg):
|
||||
self.done.clear()
|
||||
self.message = msg
|
||||
self.win.emit(SIGNAL('pin_dialog'))
|
||||
self.done.wait()
|
||||
return self.response
|
||||
|
||||
def get_passphrase(self, msg):
|
||||
self.done.clear()
|
||||
self.message = msg
|
||||
self.win.emit(SIGNAL('passphrase_dialog'))
|
||||
self.done.wait()
|
||||
return self.passphrase
|
||||
|
||||
def pin_dialog(self):
|
||||
d = QDialog(None)
|
||||
d.setModal(1)
|
||||
d.setWindowTitle(_("Enter PIN"))
|
||||
d.setWindowFlags(d.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
|
||||
matrix = PinMatrixWidget()
|
||||
vbox = QVBoxLayout()
|
||||
vbox.addWidget(QLabel(self.message))
|
||||
vbox.addWidget(matrix)
|
||||
vbox.addLayout(Buttons(CancelButton(d), OkButton(d)))
|
||||
d.setLayout(vbox)
|
||||
if not d.exec_():
|
||||
self.response = None
|
||||
self.response = str(matrix.get_value())
|
||||
self.done.set()
|
||||
|
||||
def passphrase_dialog(self):
|
||||
if type(self.win) is ElectrumWindow:
|
||||
passphrase = self.win.password_dialog(_("Please enter your Trezor passphrase"))
|
||||
self.passphrase = unicodedata.normalize('NFKD', unicode(passphrase)) if passphrase else ''
|
||||
else:
|
||||
assert type(self.win) is InstallWizard
|
||||
from electrum_gui.qt.password_dialog import make_password_dialog, run_password_dialog
|
||||
d = QDialog()
|
||||
d.setModal(1)
|
||||
d.setLayout(make_password_dialog(d, None, self.message, False))
|
||||
confirmed, p, passphrase = run_password_dialog(d, None, None)
|
||||
if not confirmed:
|
||||
QMessageBox.critical(None, _('Error'), _("Password request canceled"), _('OK'))
|
||||
self.passphrase = None
|
||||
else:
|
||||
self.passphrase = unicodedata.normalize('NFKD', unicode(passphrase)) if passphrase else ''
|
||||
self.done.set()
|
||||
|
||||
def message_dialog(self):
|
||||
self.d = QDialog()
|
||||
self.d.setModal(1)
|
||||
self.d.setWindowTitle('Please Check Trezor Device')
|
||||
self.d.setWindowFlags(self.d.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
|
||||
l = QLabel(self.message)
|
||||
vbox = QVBoxLayout(self.d)
|
||||
vbox.addWidget(l)
|
||||
self.d.show()
|
||||
|
||||
def dialog_stop(self):
|
||||
self.d.hide()
|
||||
|
||||
|
||||
class Plugin(TrezorPlugin):
|
||||
|
||||
@hook
|
||||
def load_wallet(self, wallet, window):
|
||||
self.print_error("load_wallet")
|
||||
self.wallet = wallet
|
||||
self.wallet.plugin = self
|
||||
self.trezor_button = StatusBarButton(QIcon(":icons/trezor.png"), _("Trezor"), partial(self.settings_dialog, window))
|
||||
if type(window) is ElectrumWindow:
|
||||
window.statusBar().addPermanentWidget(self.trezor_button)
|
||||
if self.handler is None:
|
||||
self.handler = TrezorQtHandler(window)
|
||||
try:
|
||||
self.get_client().ping('t')
|
||||
except BaseException as e:
|
||||
QMessageBox.information(window, _('Error'), _("Trezor device not detected.\nContinuing in watching-only mode." + '\n\nReason:\n' + str(e)), _('OK'))
|
||||
self.wallet.force_watching_only = True
|
||||
return
|
||||
if self.wallet.addresses() and not self.wallet.check_proper_device():
|
||||
QMessageBox.information(window, _('Error'), _("This wallet does not match your Trezor device"), _('OK'))
|
||||
self.wallet.force_watching_only = True
|
||||
|
||||
@hook
|
||||
def installwizard_load_wallet(self, wallet, window):
|
||||
if type(wallet) != TrezorWallet:
|
||||
return
|
||||
self.load_wallet(wallet, window)
|
||||
|
||||
@hook
|
||||
def installwizard_restore(self, wizard, storage):
|
||||
if storage.get('wallet_type') != 'trezor':
|
||||
return
|
||||
seed = wizard.enter_seed_dialog("Enter your Trezor seed", None, func=lambda x:True)
|
||||
if not seed:
|
||||
return
|
||||
wallet = TrezorWallet(storage)
|
||||
self.wallet = wallet
|
||||
handler = TrezorQtHandler(wizard)
|
||||
passphrase = handler.get_passphrase(_("Please enter your Trezor passphrase.") + '\n' + _("Press OK if you do not use one."))
|
||||
if passphrase is None:
|
||||
return
|
||||
password = wizard.password_dialog()
|
||||
wallet.add_seed(seed, password)
|
||||
wallet.add_cosigner_seed(seed, 'x/', password, passphrase)
|
||||
wallet.create_main_account(password)
|
||||
# disable trezor plugin
|
||||
self.set_enabled(False)
|
||||
return wallet
|
||||
|
||||
@hook
|
||||
def receive_menu(self, menu, addrs):
|
||||
if not self.wallet.is_watching_only() and self.atleast_version(1, 3) and len(addrs) == 1:
|
||||
menu.addAction(_("Show on TREZOR"), lambda: self.show_address(addrs[0]))
|
||||
|
||||
def show_address(self, address):
|
||||
if not self.wallet.check_proper_device():
|
||||
give_error('Wrong device or password')
|
||||
try:
|
||||
address_path = self.wallet.address_id(address)
|
||||
address_n = self.get_client().expand_path(address_path)
|
||||
except Exception, e:
|
||||
give_error(e)
|
||||
try:
|
||||
self.get_client().get_address('Bitcoin', address_n, True)
|
||||
except Exception, e:
|
||||
give_error(e)
|
||||
finally:
|
||||
self.handler.stop()
|
||||
|
||||
|
||||
def settings_dialog(self, window):
|
||||
try:
|
||||
device_id = self.get_client().get_device_id()
|
||||
except BaseException as e:
|
||||
window.show_message(str(e))
|
||||
return
|
||||
get_label = lambda: self.get_client().features.label
|
||||
update_label = lambda: current_label_label.setText("Label: %s" % get_label())
|
||||
d = QDialog()
|
||||
layout = QGridLayout(d)
|
||||
layout.addWidget(QLabel("Trezor Options"),0,0)
|
||||
layout.addWidget(QLabel("ID:"),1,0)
|
||||
layout.addWidget(QLabel(" %s" % device_id),1,1)
|
||||
|
||||
def modify_label():
|
||||
response = QInputDialog().getText(None, "Set New Trezor Label", "New Trezor Label: (upon submission confirm on Trezor)")
|
||||
if not response[1]:
|
||||
return
|
||||
new_label = str(response[0])
|
||||
self.handler.show_message("Please confirm label change on Trezor")
|
||||
status = self.get_client().apply_settings(label=new_label)
|
||||
self.handler.stop()
|
||||
update_label()
|
||||
|
||||
current_label_label = QLabel()
|
||||
update_label()
|
||||
change_label_button = QPushButton("Modify")
|
||||
change_label_button.clicked.connect(modify_label)
|
||||
layout.addWidget(current_label_label,3,0)
|
||||
layout.addWidget(change_label_button,3,1)
|
||||
d.exec_()
|
||||
|
||||
|
||||
|
||||
|
|
@ -5,7 +5,6 @@ from time import sleep
|
|||
import unicodedata
|
||||
import threading
|
||||
import re
|
||||
from functools import partial
|
||||
|
||||
|
||||
import electrum
|
||||
|
@ -31,6 +30,9 @@ except ImportError:
|
|||
|
||||
import trezorlib.ckd_public as ckd_public
|
||||
|
||||
|
||||
|
||||
|
||||
def log(msg):
|
||||
stderr.write("%s\n" % msg)
|
||||
stderr.flush()
|
||||
|
@ -200,7 +202,7 @@ class TrezorWallet(BIP32_HD_Wallet):
|
|||
|
||||
|
||||
|
||||
class Plugin(BasePlugin):
|
||||
class TrezorPlugin(BasePlugin):
|
||||
|
||||
def __init__(self, parent, config, name):
|
||||
BasePlugin.__init__(self, parent, config, name)
|
||||
|
@ -399,118 +401,6 @@ class Plugin(BasePlugin):
|
|||
return self.electrum_tx_to_txtype(tx)
|
||||
|
||||
|
||||
from PyQt4.Qt import QMessageBox, QDialog, QVBoxLayout, QLabel, QThread, SIGNAL, QGridLayout, QInputDialog, QPushButton
|
||||
import PyQt4.QtCore as QtCore
|
||||
from electrum_gui.qt.util import *
|
||||
from electrum_gui.qt.main_window import StatusBarButton, ElectrumWindow
|
||||
from electrum_gui.qt.installwizard import InstallWizard
|
||||
from trezorlib.qt.pinmatrix import PinMatrixWidget
|
||||
|
||||
class QtPlugin(Plugin):
|
||||
|
||||
@hook
|
||||
def load_wallet(self, wallet, window):
|
||||
self.print_error("load_wallet")
|
||||
self.wallet = wallet
|
||||
self.wallet.plugin = self
|
||||
self.trezor_button = StatusBarButton(QIcon(":icons/trezor.png"), _("Trezor"), partial(self.settings_dialog, window))
|
||||
if type(window) is ElectrumWindow:
|
||||
window.statusBar().addPermanentWidget(self.trezor_button)
|
||||
if self.handler is None:
|
||||
self.handler = TrezorQtHandler(window)
|
||||
try:
|
||||
self.get_client().ping('t')
|
||||
except BaseException as e:
|
||||
QMessageBox.information(window, _('Error'), _("Trezor device not detected.\nContinuing in watching-only mode." + '\n\nReason:\n' + str(e)), _('OK'))
|
||||
self.wallet.force_watching_only = True
|
||||
return
|
||||
if self.wallet.addresses() and not self.wallet.check_proper_device():
|
||||
QMessageBox.information(window, _('Error'), _("This wallet does not match your Trezor device"), _('OK'))
|
||||
self.wallet.force_watching_only = True
|
||||
|
||||
@hook
|
||||
def installwizard_load_wallet(self, wallet, window):
|
||||
if type(wallet) != TrezorWallet:
|
||||
return
|
||||
self.load_wallet(wallet, window)
|
||||
|
||||
@hook
|
||||
def installwizard_restore(self, wizard, storage):
|
||||
if storage.get('wallet_type') != 'trezor':
|
||||
return
|
||||
seed = wizard.enter_seed_dialog("Enter your Trezor seed", None, func=lambda x:True)
|
||||
if not seed:
|
||||
return
|
||||
wallet = TrezorWallet(storage)
|
||||
self.wallet = wallet
|
||||
handler = TrezorQtHandler(wizard)
|
||||
passphrase = handler.get_passphrase(_("Please enter your Trezor passphrase.") + '\n' + _("Press OK if you do not use one."))
|
||||
if passphrase is None:
|
||||
return
|
||||
password = wizard.password_dialog()
|
||||
wallet.add_seed(seed, password)
|
||||
wallet.add_cosigner_seed(seed, 'x/', password, passphrase)
|
||||
wallet.create_main_account(password)
|
||||
# disable trezor plugin
|
||||
self.set_enabled(False)
|
||||
return wallet
|
||||
|
||||
@hook
|
||||
def receive_menu(self, menu, addrs):
|
||||
if not self.wallet.is_watching_only() and self.atleast_version(1, 3) and len(addrs) == 1:
|
||||
menu.addAction(_("Show on TREZOR"), lambda: self.show_address(addrs[0]))
|
||||
|
||||
def show_address(self, address):
|
||||
if not self.wallet.check_proper_device():
|
||||
give_error('Wrong device or password')
|
||||
try:
|
||||
address_path = self.wallet.address_id(address)
|
||||
address_n = self.get_client().expand_path(address_path)
|
||||
except Exception, e:
|
||||
give_error(e)
|
||||
try:
|
||||
self.get_client().get_address('Bitcoin', address_n, True)
|
||||
except Exception, e:
|
||||
give_error(e)
|
||||
finally:
|
||||
self.handler.stop()
|
||||
|
||||
|
||||
def settings_dialog(self, window):
|
||||
try:
|
||||
device_id = self.get_client().get_device_id()
|
||||
except BaseException as e:
|
||||
window.show_message(str(e))
|
||||
return
|
||||
get_label = lambda: self.get_client().features.label
|
||||
update_label = lambda: current_label_label.setText("Label: %s" % get_label())
|
||||
d = QDialog()
|
||||
layout = QGridLayout(d)
|
||||
layout.addWidget(QLabel("Trezor Options"),0,0)
|
||||
layout.addWidget(QLabel("ID:"),1,0)
|
||||
layout.addWidget(QLabel(" %s" % device_id),1,1)
|
||||
|
||||
def modify_label():
|
||||
response = QInputDialog().getText(None, "Set New Trezor Label", "New Trezor Label: (upon submission confirm on Trezor)")
|
||||
if not response[1]:
|
||||
return
|
||||
new_label = str(response[0])
|
||||
self.handler.show_message("Please confirm label change on Trezor")
|
||||
status = self.get_client().apply_settings(label=new_label)
|
||||
self.handler.stop()
|
||||
update_label()
|
||||
|
||||
current_label_label = QLabel()
|
||||
update_label()
|
||||
change_label_button = QPushButton("Modify")
|
||||
change_label_button.clicked.connect(modify_label)
|
||||
layout.addWidget(current_label_label,3,0)
|
||||
layout.addWidget(change_label_button,3,1)
|
||||
d.exec_()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -582,83 +472,6 @@ class TrezorCmdLineHandler:
|
|||
print_msg(msg)
|
||||
|
||||
|
||||
class TrezorQtHandler:
|
||||
|
||||
def __init__(self, win):
|
||||
self.win = win
|
||||
self.win.connect(win, SIGNAL('trezor_done'), self.dialog_stop)
|
||||
self.win.connect(win, SIGNAL('message_dialog'), self.message_dialog)
|
||||
self.win.connect(win, SIGNAL('pin_dialog'), self.pin_dialog)
|
||||
self.win.connect(win, SIGNAL('passphrase_dialog'), self.passphrase_dialog)
|
||||
self.done = threading.Event()
|
||||
|
||||
def stop(self):
|
||||
self.win.emit(SIGNAL('trezor_done'))
|
||||
|
||||
def show_message(self, msg):
|
||||
self.message = msg
|
||||
self.win.emit(SIGNAL('message_dialog'))
|
||||
|
||||
def get_pin(self, msg):
|
||||
self.done.clear()
|
||||
self.message = msg
|
||||
self.win.emit(SIGNAL('pin_dialog'))
|
||||
self.done.wait()
|
||||
return self.response
|
||||
|
||||
def get_passphrase(self, msg):
|
||||
self.done.clear()
|
||||
self.message = msg
|
||||
self.win.emit(SIGNAL('passphrase_dialog'))
|
||||
self.done.wait()
|
||||
return self.passphrase
|
||||
|
||||
def pin_dialog(self):
|
||||
d = QDialog(None)
|
||||
d.setModal(1)
|
||||
d.setWindowTitle(_("Enter PIN"))
|
||||
d.setWindowFlags(d.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
|
||||
matrix = PinMatrixWidget()
|
||||
vbox = QVBoxLayout()
|
||||
vbox.addWidget(QLabel(self.message))
|
||||
vbox.addWidget(matrix)
|
||||
vbox.addLayout(Buttons(CancelButton(d), OkButton(d)))
|
||||
d.setLayout(vbox)
|
||||
if not d.exec_():
|
||||
self.response = None
|
||||
self.response = str(matrix.get_value())
|
||||
self.done.set()
|
||||
|
||||
def passphrase_dialog(self):
|
||||
if type(self.win) is ElectrumWindow:
|
||||
passphrase = self.win.password_dialog(_("Please enter your Trezor passphrase"))
|
||||
self.passphrase = unicodedata.normalize('NFKD', unicode(passphrase)) if passphrase else ''
|
||||
else:
|
||||
assert type(self.win) is InstallWizard
|
||||
from electrum_gui.qt.password_dialog import make_password_dialog, run_password_dialog
|
||||
d = QDialog()
|
||||
d.setModal(1)
|
||||
d.setLayout(make_password_dialog(d, None, self.message, False))
|
||||
confirmed, p, passphrase = run_password_dialog(d, None, None)
|
||||
if not confirmed:
|
||||
QMessageBox.critical(None, _('Error'), _("Password request canceled"), _('OK'))
|
||||
self.passphrase = None
|
||||
else:
|
||||
self.passphrase = unicodedata.normalize('NFKD', unicode(passphrase)) if passphrase else ''
|
||||
self.done.set()
|
||||
|
||||
def message_dialog(self):
|
||||
self.d = QDialog()
|
||||
self.d.setModal(1)
|
||||
self.d.setWindowTitle('Please Check Trezor Device')
|
||||
self.d.setWindowFlags(self.d.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
|
||||
l = QLabel(self.message)
|
||||
vbox = QVBoxLayout(self.d)
|
||||
vbox.addWidget(l)
|
||||
self.d.show()
|
||||
|
||||
def dialog_stop(self):
|
||||
self.d.hide()
|
||||
|
||||
|
||||
if TREZOR:
|
11
plugins/trustedcoin/__init__.py
Normal file
11
plugins/trustedcoin/__init__.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
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 = ('twofactor', '2fa', _("Wallet with two-factor authentication"))
|
||||
available_for = ['qt', 'cmdline']
|
225
plugins/trustedcoin/qt.py
Normal file
225
plugins/trustedcoin/qt.py
Normal file
|
@ -0,0 +1,225 @@
|
|||
from PyQt4.QtGui import *
|
||||
from PyQt4.QtCore import *
|
||||
from electrum_gui.qt.util import *
|
||||
from electrum_gui.qt.qrcodewidget import QRCodeWidget
|
||||
from electrum_gui.qt.amountedit import AmountEdit
|
||||
from electrum_gui.qt.main_window import StatusBarButton
|
||||
|
||||
class Plugin(TrustedCoinPlugin):
|
||||
|
||||
def auth_dialog(self, window):
|
||||
d = QDialog(window)
|
||||
d.setModal(1)
|
||||
vbox = QVBoxLayout(d)
|
||||
pw = AmountEdit(None, is_int = True)
|
||||
msg = _('Please enter your Google Authenticator code')
|
||||
vbox.addWidget(QLabel(msg))
|
||||
grid = QGridLayout()
|
||||
grid.setSpacing(8)
|
||||
grid.addWidget(QLabel(_('Code')), 1, 0)
|
||||
grid.addWidget(pw, 1, 1)
|
||||
vbox.addLayout(grid)
|
||||
vbox.addLayout(Buttons(CancelButton(d), OkButton(d)))
|
||||
if not d.exec_():
|
||||
return
|
||||
return pw.get_amount()
|
||||
|
||||
@hook
|
||||
def sign_tx(self, window, tx):
|
||||
self.print_error("twofactor:sign_tx")
|
||||
wallet = window.wallet
|
||||
if type(wallet) is Wallet_2fa and not wallet.can_sign_without_server():
|
||||
auth_code = None
|
||||
if need_server(wallet, tx):
|
||||
auth_code = self.auth_dialog(window)
|
||||
else:
|
||||
self.print_error("twofactor: xpub3 not needed")
|
||||
window.wallet.auth_code = auth_code
|
||||
|
||||
@hook
|
||||
def abort_send(self, window):
|
||||
wallet = window.wallet
|
||||
if type(wallet) is Wallet_2fa and not wallet.can_sign_without_server():
|
||||
if wallet.billing_info is None:
|
||||
# request billing info before forming the transaction
|
||||
task = partial(self.request_billing_info, wallet)
|
||||
waiting_dialog = WaitingDialog(window, 'please wait...', task)
|
||||
waiting_dialog.start()
|
||||
waiting_dialog.wait()
|
||||
if wallet.billing_info is None:
|
||||
window.show_message('Could not contact server')
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def settings_dialog(self, window):
|
||||
task = partial(self.request_billing_info, window.wallet)
|
||||
self.waiting_dialog = WaitingDialog(window, 'please wait...', task, partial(self.show_settings_dialog, window))
|
||||
self.waiting_dialog.start()
|
||||
|
||||
def show_settings_dialog(self, window, success):
|
||||
if not success:
|
||||
window.show_message(_('Server not reachable.'))
|
||||
return
|
||||
|
||||
wallet = window.wallet
|
||||
d = QDialog(window)
|
||||
d.setWindowTitle("TrustedCoin Information")
|
||||
d.setMinimumSize(500, 200)
|
||||
vbox = QVBoxLayout(d)
|
||||
hbox = QHBoxLayout()
|
||||
|
||||
logo = QLabel()
|
||||
logo.setPixmap(QPixmap(":icons/trustedcoin.png"))
|
||||
msg = _('This wallet is protected by TrustedCoin\'s two-factor authentication.') + '<br/>'\
|
||||
+ _("For more information, visit") + " <a href=\"https://api.trustedcoin.com/#/electrum-help\">https://api.trustedcoin.com/#/electrum-help</a>"
|
||||
label = QLabel(msg)
|
||||
label.setOpenExternalLinks(1)
|
||||
|
||||
hbox.addStretch(10)
|
||||
hbox.addWidget(logo)
|
||||
hbox.addStretch(10)
|
||||
hbox.addWidget(label)
|
||||
hbox.addStretch(10)
|
||||
|
||||
vbox.addLayout(hbox)
|
||||
vbox.addStretch(10)
|
||||
|
||||
msg = _('TrustedCoin charges a fee per co-signed transaction. You may pay on each transaction (an extra output will be added to your transaction), or you may purchase prepaid transaction using this dialog.') + '<br/>'
|
||||
label = QLabel(msg)
|
||||
label.setWordWrap(1)
|
||||
vbox.addWidget(label)
|
||||
|
||||
vbox.addStretch(10)
|
||||
grid = QGridLayout()
|
||||
vbox.addLayout(grid)
|
||||
|
||||
price_per_tx = wallet.price_per_tx
|
||||
v = price_per_tx.get(1)
|
||||
grid.addWidget(QLabel(_("Price per transaction (not prepaid):")), 0, 0)
|
||||
grid.addWidget(QLabel(window.format_amount(v) + ' ' + window.base_unit()), 0, 1)
|
||||
|
||||
i = 1
|
||||
|
||||
if 10 not in price_per_tx:
|
||||
price_per_tx[10] = 10 * price_per_tx.get(1)
|
||||
|
||||
for k, v in sorted(price_per_tx.items()):
|
||||
if k == 1:
|
||||
continue
|
||||
grid.addWidget(QLabel("Price for %d prepaid transactions:"%k), i, 0)
|
||||
grid.addWidget(QLabel("%d x "%k + window.format_amount(v/k) + ' ' + window.base_unit()), i, 1)
|
||||
b = QPushButton(_("Buy"))
|
||||
b.clicked.connect(lambda b, k=k, v=v: self.on_buy(window, k, v, d))
|
||||
grid.addWidget(b, i, 2)
|
||||
i += 1
|
||||
|
||||
n = wallet.billing_info.get('tx_remaining', 0)
|
||||
grid.addWidget(QLabel(_("Your wallet has %d prepaid transactions.")%n), i, 0)
|
||||
|
||||
# tranfer button
|
||||
#def on_transfer():
|
||||
# server.transfer_credit(self.user_id, recipient, otp, signature_callback)
|
||||
# pass
|
||||
#b = QPushButton(_("Transfer"))
|
||||
#b.clicked.connect(on_transfer)
|
||||
#grid.addWidget(b, 1, 2)
|
||||
|
||||
#grid.addWidget(QLabel(_("Next Billing Address:")), i, 0)
|
||||
#grid.addWidget(QLabel(self.billing_info['billing_address']), i, 1)
|
||||
vbox.addLayout(Buttons(CloseButton(d)))
|
||||
d.exec_()
|
||||
|
||||
def on_buy(self, window, k, v, d):
|
||||
d.close()
|
||||
if window.pluginsdialog:
|
||||
window.pluginsdialog.close()
|
||||
wallet = window.wallet
|
||||
uri = "bitcoin:" + wallet.billing_info['billing_address'] + "?message=TrustedCoin %d Prepaid Transactions&amount="%k + str(Decimal(v)/100000000)
|
||||
wallet.is_billing = True
|
||||
window.pay_to_URI(uri)
|
||||
window.payto_e.setFrozen(True)
|
||||
window.message_e.setFrozen(True)
|
||||
window.amount_e.setFrozen(True)
|
||||
|
||||
def accept_terms_of_use(self, window):
|
||||
vbox = QVBoxLayout()
|
||||
window.set_layout(vbox)
|
||||
vbox.addWidget(QLabel(_("Terms of Service")))
|
||||
|
||||
tos_e = QTextEdit()
|
||||
tos_e.setReadOnly(True)
|
||||
vbox.addWidget(tos_e)
|
||||
|
||||
vbox.addWidget(QLabel(_("Please enter your e-mail address")))
|
||||
email_e = QLineEdit()
|
||||
vbox.addWidget(email_e)
|
||||
vbox.addStretch()
|
||||
accept_button = OkButton(window, _('Accept'))
|
||||
accept_button.setEnabled(False)
|
||||
vbox.addLayout(Buttons(CancelButton(window), accept_button))
|
||||
|
||||
def request_TOS():
|
||||
tos = server.get_terms_of_service()
|
||||
self.TOS = tos
|
||||
window.emit(SIGNAL('twofactor:TOS'))
|
||||
|
||||
def on_result():
|
||||
tos_e.setText(self.TOS)
|
||||
|
||||
window.connect(window, SIGNAL('twofactor:TOS'), on_result)
|
||||
t = Thread(target=request_TOS)
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
||||
regexp = r"[^@]+@[^@]+\.[^@]+"
|
||||
email_e.textChanged.connect(lambda: accept_button.setEnabled(re.match(regexp,email_e.text()) is not None))
|
||||
email_e.setFocus(True)
|
||||
|
||||
if not window.exec_():
|
||||
return
|
||||
|
||||
email = str(email_e.text())
|
||||
return email
|
||||
|
||||
|
||||
def setup_google_auth(self, window, _id, otp_secret):
|
||||
vbox = QVBoxLayout()
|
||||
window.set_layout(vbox)
|
||||
if otp_secret is not None:
|
||||
uri = "otpauth://totp/%s?secret=%s"%('trustedcoin.com', otp_secret)
|
||||
vbox.addWidget(QLabel("Please scan this QR code in Google Authenticator."))
|
||||
qrw = QRCodeWidget(uri)
|
||||
vbox.addWidget(qrw, 1)
|
||||
msg = _('Then, enter your Google Authenticator code:')
|
||||
else:
|
||||
label = QLabel("This wallet is already registered, but it was never authenticated. To finalize your registration, please enter your Google Authenticator Code. If you do not have this code, delete the wallet file and start a new registration")
|
||||
label.setWordWrap(1)
|
||||
vbox.addWidget(label)
|
||||
msg = _('Google Authenticator code:')
|
||||
|
||||
hbox = QHBoxLayout()
|
||||
hbox.addWidget(QLabel(msg))
|
||||
pw = AmountEdit(None, is_int = True)
|
||||
pw.setFocus(True)
|
||||
hbox.addWidget(pw)
|
||||
hbox.addStretch(1)
|
||||
vbox.addLayout(hbox)
|
||||
|
||||
b = OkButton(window, _('Next'))
|
||||
b.setEnabled(False)
|
||||
vbox.addLayout(Buttons(CancelButton(window), b))
|
||||
pw.textChanged.connect(lambda: b.setEnabled(len(pw.text())==6))
|
||||
|
||||
while True:
|
||||
if not window.exec_():
|
||||
return False
|
||||
otp = pw.get_amount()
|
||||
try:
|
||||
server.auth(_id, otp)
|
||||
return True
|
||||
except:
|
||||
QMessageBox.information(window, _('Message'), _('Incorrect password'), _('OK'))
|
||||
pw.setText('')
|
||||
|
||||
|
|
@ -283,7 +283,8 @@ def need_server(wallet, tx):
|
|||
return True
|
||||
return False
|
||||
|
||||
class Plugin(BasePlugin):
|
||||
|
||||
class TrustedCoinPlugin(BasePlugin):
|
||||
|
||||
def __init__(self, parent, config, name):
|
||||
BasePlugin.__init__(self, parent, config, name)
|
||||
|
@ -454,228 +455,3 @@ class Plugin(BasePlugin):
|
|||
return True
|
||||
|
||||
|
||||
from PyQt4.QtGui import *
|
||||
from PyQt4.QtCore import *
|
||||
from electrum_gui.qt.util import *
|
||||
from electrum_gui.qt.qrcodewidget import QRCodeWidget
|
||||
from electrum_gui.qt.amountedit import AmountEdit
|
||||
from electrum_gui.qt.main_window import StatusBarButton
|
||||
|
||||
class QtPlugin(Plugin):
|
||||
|
||||
def auth_dialog(self, window):
|
||||
d = QDialog(window)
|
||||
d.setModal(1)
|
||||
vbox = QVBoxLayout(d)
|
||||
pw = AmountEdit(None, is_int = True)
|
||||
msg = _('Please enter your Google Authenticator code')
|
||||
vbox.addWidget(QLabel(msg))
|
||||
grid = QGridLayout()
|
||||
grid.setSpacing(8)
|
||||
grid.addWidget(QLabel(_('Code')), 1, 0)
|
||||
grid.addWidget(pw, 1, 1)
|
||||
vbox.addLayout(grid)
|
||||
vbox.addLayout(Buttons(CancelButton(d), OkButton(d)))
|
||||
if not d.exec_():
|
||||
return
|
||||
return pw.get_amount()
|
||||
|
||||
@hook
|
||||
def sign_tx(self, window, tx):
|
||||
self.print_error("twofactor:sign_tx")
|
||||
wallet = window.wallet
|
||||
if type(wallet) is Wallet_2fa and not wallet.can_sign_without_server():
|
||||
auth_code = None
|
||||
if need_server(wallet, tx):
|
||||
auth_code = self.auth_dialog(window)
|
||||
else:
|
||||
self.print_error("twofactor: xpub3 not needed")
|
||||
window.wallet.auth_code = auth_code
|
||||
|
||||
@hook
|
||||
def abort_send(self, window):
|
||||
wallet = window.wallet
|
||||
if type(wallet) is Wallet_2fa and not wallet.can_sign_without_server():
|
||||
if wallet.billing_info is None:
|
||||
# request billing info before forming the transaction
|
||||
task = partial(self.request_billing_info, wallet)
|
||||
waiting_dialog = WaitingDialog(window, 'please wait...', task)
|
||||
waiting_dialog.start()
|
||||
waiting_dialog.wait()
|
||||
if wallet.billing_info is None:
|
||||
window.show_message('Could not contact server')
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def settings_dialog(self, window):
|
||||
task = partial(self.request_billing_info, window.wallet)
|
||||
self.waiting_dialog = WaitingDialog(window, 'please wait...', task, partial(self.show_settings_dialog, window))
|
||||
self.waiting_dialog.start()
|
||||
|
||||
def show_settings_dialog(self, window, success):
|
||||
if not success:
|
||||
window.show_message(_('Server not reachable.'))
|
||||
return
|
||||
|
||||
wallet = window.wallet
|
||||
d = QDialog(window)
|
||||
d.setWindowTitle("TrustedCoin Information")
|
||||
d.setMinimumSize(500, 200)
|
||||
vbox = QVBoxLayout(d)
|
||||
hbox = QHBoxLayout()
|
||||
|
||||
logo = QLabel()
|
||||
logo.setPixmap(QPixmap(":icons/trustedcoin.png"))
|
||||
msg = _('This wallet is protected by TrustedCoin\'s two-factor authentication.') + '<br/>'\
|
||||
+ _("For more information, visit") + " <a href=\"https://api.trustedcoin.com/#/electrum-help\">https://api.trustedcoin.com/#/electrum-help</a>"
|
||||
label = QLabel(msg)
|
||||
label.setOpenExternalLinks(1)
|
||||
|
||||
hbox.addStretch(10)
|
||||
hbox.addWidget(logo)
|
||||
hbox.addStretch(10)
|
||||
hbox.addWidget(label)
|
||||
hbox.addStretch(10)
|
||||
|
||||
vbox.addLayout(hbox)
|
||||
vbox.addStretch(10)
|
||||
|
||||
msg = _('TrustedCoin charges a fee per co-signed transaction. You may pay on each transaction (an extra output will be added to your transaction), or you may purchase prepaid transaction using this dialog.') + '<br/>'
|
||||
label = QLabel(msg)
|
||||
label.setWordWrap(1)
|
||||
vbox.addWidget(label)
|
||||
|
||||
vbox.addStretch(10)
|
||||
grid = QGridLayout()
|
||||
vbox.addLayout(grid)
|
||||
|
||||
price_per_tx = wallet.price_per_tx
|
||||
v = price_per_tx.get(1)
|
||||
grid.addWidget(QLabel(_("Price per transaction (not prepaid):")), 0, 0)
|
||||
grid.addWidget(QLabel(window.format_amount(v) + ' ' + window.base_unit()), 0, 1)
|
||||
|
||||
i = 1
|
||||
|
||||
if 10 not in price_per_tx:
|
||||
price_per_tx[10] = 10 * price_per_tx.get(1)
|
||||
|
||||
for k, v in sorted(price_per_tx.items()):
|
||||
if k == 1:
|
||||
continue
|
||||
grid.addWidget(QLabel("Price for %d prepaid transactions:"%k), i, 0)
|
||||
grid.addWidget(QLabel("%d x "%k + window.format_amount(v/k) + ' ' + window.base_unit()), i, 1)
|
||||
b = QPushButton(_("Buy"))
|
||||
b.clicked.connect(lambda b, k=k, v=v: self.on_buy(window, k, v, d))
|
||||
grid.addWidget(b, i, 2)
|
||||
i += 1
|
||||
|
||||
n = wallet.billing_info.get('tx_remaining', 0)
|
||||
grid.addWidget(QLabel(_("Your wallet has %d prepaid transactions.")%n), i, 0)
|
||||
|
||||
# tranfer button
|
||||
#def on_transfer():
|
||||
# server.transfer_credit(self.user_id, recipient, otp, signature_callback)
|
||||
# pass
|
||||
#b = QPushButton(_("Transfer"))
|
||||
#b.clicked.connect(on_transfer)
|
||||
#grid.addWidget(b, 1, 2)
|
||||
|
||||
#grid.addWidget(QLabel(_("Next Billing Address:")), i, 0)
|
||||
#grid.addWidget(QLabel(self.billing_info['billing_address']), i, 1)
|
||||
vbox.addLayout(Buttons(CloseButton(d)))
|
||||
d.exec_()
|
||||
|
||||
def on_buy(self, window, k, v, d):
|
||||
d.close()
|
||||
if window.pluginsdialog:
|
||||
window.pluginsdialog.close()
|
||||
wallet = window.wallet
|
||||
uri = "bitcoin:" + wallet.billing_info['billing_address'] + "?message=TrustedCoin %d Prepaid Transactions&amount="%k + str(Decimal(v)/100000000)
|
||||
wallet.is_billing = True
|
||||
window.pay_to_URI(uri)
|
||||
window.payto_e.setFrozen(True)
|
||||
window.message_e.setFrozen(True)
|
||||
window.amount_e.setFrozen(True)
|
||||
|
||||
def accept_terms_of_use(self, window):
|
||||
vbox = QVBoxLayout()
|
||||
window.set_layout(vbox)
|
||||
vbox.addWidget(QLabel(_("Terms of Service")))
|
||||
|
||||
tos_e = QTextEdit()
|
||||
tos_e.setReadOnly(True)
|
||||
vbox.addWidget(tos_e)
|
||||
|
||||
vbox.addWidget(QLabel(_("Please enter your e-mail address")))
|
||||
email_e = QLineEdit()
|
||||
vbox.addWidget(email_e)
|
||||
vbox.addStretch()
|
||||
accept_button = OkButton(window, _('Accept'))
|
||||
accept_button.setEnabled(False)
|
||||
vbox.addLayout(Buttons(CancelButton(window), accept_button))
|
||||
|
||||
def request_TOS():
|
||||
tos = server.get_terms_of_service()
|
||||
self.TOS = tos
|
||||
window.emit(SIGNAL('twofactor:TOS'))
|
||||
|
||||
def on_result():
|
||||
tos_e.setText(self.TOS)
|
||||
|
||||
window.connect(window, SIGNAL('twofactor:TOS'), on_result)
|
||||
t = Thread(target=request_TOS)
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
||||
regexp = r"[^@]+@[^@]+\.[^@]+"
|
||||
email_e.textChanged.connect(lambda: accept_button.setEnabled(re.match(regexp,email_e.text()) is not None))
|
||||
email_e.setFocus(True)
|
||||
|
||||
if not window.exec_():
|
||||
return
|
||||
|
||||
email = str(email_e.text())
|
||||
return email
|
||||
|
||||
|
||||
def setup_google_auth(self, window, _id, otp_secret):
|
||||
vbox = QVBoxLayout()
|
||||
window.set_layout(vbox)
|
||||
if otp_secret is not None:
|
||||
uri = "otpauth://totp/%s?secret=%s"%('trustedcoin.com', otp_secret)
|
||||
vbox.addWidget(QLabel("Please scan this QR code in Google Authenticator."))
|
||||
qrw = QRCodeWidget(uri)
|
||||
vbox.addWidget(qrw, 1)
|
||||
msg = _('Then, enter your Google Authenticator code:')
|
||||
else:
|
||||
label = QLabel("This wallet is already registered, but it was never authenticated. To finalize your registration, please enter your Google Authenticator Code. If you do not have this code, delete the wallet file and start a new registration")
|
||||
label.setWordWrap(1)
|
||||
vbox.addWidget(label)
|
||||
msg = _('Google Authenticator code:')
|
||||
|
||||
hbox = QHBoxLayout()
|
||||
hbox.addWidget(QLabel(msg))
|
||||
pw = AmountEdit(None, is_int = True)
|
||||
pw.setFocus(True)
|
||||
hbox.addWidget(pw)
|
||||
hbox.addStretch(1)
|
||||
vbox.addLayout(hbox)
|
||||
|
||||
b = OkButton(window, _('Next'))
|
||||
b.setEnabled(False)
|
||||
vbox.addLayout(Buttons(CancelButton(window), b))
|
||||
pw.textChanged.connect(lambda: b.setEnabled(len(pw.text())==6))
|
||||
|
||||
while True:
|
||||
if not window.exec_():
|
||||
return False
|
||||
otp = pw.get_amount()
|
||||
try:
|
||||
server.auth(_id, otp)
|
||||
return True
|
||||
except:
|
||||
QMessageBox.information(window, _('Message'), _('Incorrect password'), _('OK'))
|
||||
pw.setText('')
|
||||
|
||||
|
5
plugins/virtualkeyboard/__init__.py
Normal file
5
plugins/virtualkeyboard/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from electrum.i18n import _
|
||||
|
||||
fullname = 'Virtual Keyboard'
|
||||
description = '%s\n%s' % (_("Add an optional virtual keyboard to the password dialog."), _("Warning: do not use this if it makes you pick a weaker password."))
|
||||
available_for = ['qt']
|
|
@ -3,7 +3,7 @@ from electrum.plugins import BasePlugin, hook
|
|||
from electrum.i18n import _
|
||||
import random
|
||||
|
||||
class QtPlugin(BasePlugin):
|
||||
class Plugin(BasePlugin):
|
||||
|
||||
vkb = None
|
||||
vkb_index = 0
|
18
setup.py
18
setup.py
|
@ -34,12 +34,28 @@ setup(
|
|||
'protobuf',
|
||||
'dnspython',
|
||||
],
|
||||
packages=[
|
||||
'electrum',
|
||||
'electrum_gui',
|
||||
'electrum_gui.qt',
|
||||
'electrum_plugins.audio_modem',
|
||||
'electrum_plugins.cosigner_pool',
|
||||
'electrum_plugins.email_requests',
|
||||
'electrum_plugins.exchange_rate',
|
||||
'electrum_plugins.greenaddress_instant',
|
||||
'electrum_plugins.keepkey',
|
||||
'electrum_plugins.labels',
|
||||
'electrum_plugins.ledger',
|
||||
'electrum_plugins.plot',
|
||||
'electrum_plugins.trezor',
|
||||
'electrum_plugins.trustedcoin',
|
||||
'electrum_plugins.virtualkeyboard',
|
||||
],
|
||||
package_dir={
|
||||
'electrum': 'lib',
|
||||
'electrum_gui': 'gui',
|
||||
'electrum_plugins': 'plugins',
|
||||
},
|
||||
packages=['electrum','electrum_gui','electrum_gui.qt','electrum_plugins'],
|
||||
package_data={
|
||||
'electrum': [
|
||||
'www/index.html',
|
||||
|
|
Loading…
Add table
Reference in a new issue