commit 2c38e85a5032e82fd666ecb00eab4d7d5193982e
parent 101fe08ac8270631e484ab06785d663d3f872673
Author: ThomasV <thomasv@gitorious>
Date: Tue, 1 Jul 2014 18:46:11 +0200
better integration of plugins in installwizard (for 2fa, trezor plugins)
Diffstat:
4 files changed, 150 insertions(+), 201 deletions(-)
diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py
@@ -4,7 +4,8 @@ import PyQt4.QtCore as QtCore
from electrum.i18n import _
from electrum import Wallet, Wallet_2of2, Wallet_2of3
-import electrum.bitcoin as bitcoin
+from electrum import bitcoin
+from electrum import util
import seed_dialog
from network_dialog import NetworkDialog
@@ -88,47 +89,40 @@ class InstallWizard(QDialog):
label2 = ClickableLabel(_("Wallet type:") + " [+]")
hbox = QHBoxLayout()
hbox.addWidget(label2)
- grid2.addLayout(hbox, 3, 0)
+ grid2.addLayout(hbox, 0, 0)
gb2 = QGroupBox()
grid.addWidget(gb2, 3, 0)
-
group2 = QButtonGroup()
- bb1 = QRadioButton(gb2)
- bb1.setText(_("Standard wallet"))
- bb1.setChecked(True)
-
- bb2 = QRadioButton(gb2)
- bb2.setText(_("Wallet with two-factor authentication (plugin)"))
-
- bb3 = QRadioButton(gb2)
- bb3.setText(_("Multisig wallet (2 of 2)"))
- bb3.setHidden(True)
+ self.wallet_types = [
+ ('standard', _("Standard wallet"), Wallet),
+ ('2of2', _("Multisig wallet (2 of 2)"), Wallet_2of2),
+ ('2of3', _("Multisig wallet (2 of 3)"), Wallet_2of3)
+ ]
+ run_hook('add_wallet_types', self.wallet_types)
+
+ for i, (t,l,c) in enumerate(self.wallet_types):
+ button = QRadioButton(gb2)
+ button.setText(l)
+ grid2.addWidget(button, i+1, 0)
+ group2.addButton(button)
+ group2.setId(button, i)
+ if i==0:
+ button.setChecked(True)
+ #else:
+ # button.setHidden(True)
- bb4 = QRadioButton(gb2)
- bb4.setText(_("Multisig wallet (2 of 3)"))
- bb4.setHidden(True)
-
- grid2.addWidget(bb1, 4, 0)
- grid2.addWidget(bb2, 5, 0)
- grid2.addWidget(bb3, 6, 0)
- grid2.addWidget(bb4, 7, 0)
def toggle():
- x = not bb3.isHidden()
+ buttons = group2.buttons()
+ x = buttons[1].isHidden()
label2.setText(_("Wallet type:") + (' [+]' if x else ' [-]'))
- bb3.setHidden(x)
- bb4.setHidden(x)
-
- self.connect(label2, SIGNAL('clicked()'), toggle)
+ for b in buttons[1:]:
+ b.setHidden(not x)
+ self.connect(label2, SIGNAL('clicked()'), toggle)
grid2.addWidget(label2)
-
- group2.addButton(bb1)
- group2.addButton(bb2)
- group2.addButton(bb3)
- group2.addButton(bb4)
vbox.addLayout(grid2)
vbox.addStretch(1)
@@ -143,17 +137,8 @@ class InstallWizard(QDialog):
return None, None
action = 'create' if b1.isChecked() else 'restore'
-
- if bb1.isChecked():
- t = 'standard'
- elif bb2.isChecked():
- t = '2fa'
- elif bb3.isChecked():
- t = '2of2'
- elif bb4.isChecked():
- t = '2of3'
-
- return action, t
+ wallet_type = self.wallet_types[group2.checkedId()][0]
+ return action, wallet_type
def verify_seed(self, seed, sid):
@@ -173,7 +158,6 @@ class InstallWizard(QDialog):
text = ' '.join(text.split())
return text
-
def is_any(self, seed_e):
text = self.get_seed_text(seed_e)
return Wallet.is_seed(text) or Wallet.is_old_mpk(text) or Wallet.is_xpub(text) or Wallet.is_xprv(text) or Wallet.is_address(text) or Wallet.is_private_key(text)
@@ -182,6 +166,9 @@ class InstallWizard(QDialog):
text = self.get_seed_text(seed_e)
return Wallet.is_xpub(text) or Wallet.is_old_mpk(text)
+ def is_xpub(self, seed_e):
+ text = self.get_seed_text(seed_e)
+ return Wallet.is_xpub(text)
def enter_seed_dialog(self, msg, sid):
vbox, seed_e = seed_dialog.enter_seed_box(msg, sid)
@@ -385,87 +372,97 @@ class InstallWizard(QDialog):
def run(self, action):
if action == 'new':
- action, t = self.restore_or_create()
+ action, wallet_type = self.restore_or_create()
+ self.storage.put('wallet_type', wallet_type, False)
- if action is None:
+ if action is None:
return
-
- if action == 'create':
- if t == 'standard':
- wallet = Wallet(self.storage)
- elif t == '2fa':
- wallet = Wallet_2of3(self.storage)
- run_hook('create_cold_seed', wallet, self)
- self.create_cold_seed(wallet)
+ if action == 'restore':
+ wallet = self.restore(wallet_type)
+ if not wallet:
return
+ action = None
- elif t == '2of2':
- wallet = Wallet_2of2(self.storage)
- action = 'create_2of2_1'
+ else:
+ wallet = Wallet(self.storage)
+ action = wallet.get_action()
+ # fixme: password is only needed for multiple accounts
+ password = None
- elif t == '2of3':
- wallet = Wallet_2of3(self.storage)
- action = 'create_2of3_1'
+ while action is not None:
+
+ util.print_error("installwizard:", wallet, action)
+ if action == 'create_seed':
+ seed = wallet.make_seed()
+ if not self.show_seed(seed, None):
+ return
+ if not self.verify_seed(seed, None):
+ return
+ password = self.password_dialog()
+ wallet.add_seed(seed, password)
- if action in ['create_2fa_2', 'create_2of3_2']:
- wallet = Wallet_2of3(self.storage)
+ elif action == 'add_cosigner':
+ xpub_hot = wallet.master_public_keys.get("m/")
+ r = self.multi_mpk_dialog(xpub_hot, 1)
+ if not r:
+ return
+ xpub_cold = r[0]
+ wallet.add_master_public_key("cold/", xpub_cold)
- if action in ['create_2of2_2']:
- wallet = Wallet_2of2(self.storage)
+ elif action == 'add_two_cosigners':
+ xpub_hot = wallet.master_public_keys.get("m/")
+ r = self.multi_mpk_dialog(xpub_hot, 2)
+ if not r:
+ return
+ xpub1, xpub2 = r
+ wallet.add_master_public_key("cold/", xpub1)
+ wallet.add_master_public_key("remote/", xpub2)
- if action in ['create', 'create_2of2_1', 'create_2fa_2', 'create_2of3_1']:
- seed = wallet.make_seed()
- sid = None if action == 'create' else 'hot'
- if not self.show_seed(seed, sid):
- return
- if not self.verify_seed(seed, sid):
- return
- password = self.password_dialog()
- wallet.add_seed(seed, password)
- if action == 'create':
+ elif action == 'create_accounts':
wallet.create_accounts(password)
self.waiting_dialog(wallet.synchronize)
- elif action == 'create_2of2_1':
- action = 'create_2of2_2'
- elif action == 'create_2of3_1':
- action = 'create_2of3_2'
- elif action == 'create_2fa_2':
- action = 'create_2fa_3'
-
- if action == 'create_2of2_2':
- xpub_hot = wallet.master_public_keys.get("m/")
- r = self.multi_mpk_dialog(xpub_hot, 1)
- if not r:
+
+ elif action == 'create_cold_seed':
+ self.create_cold_seed(wallet)
return
- xpub_cold = r[0]
- wallet.add_master_public_key("cold/", xpub_cold)
- wallet.create_account()
- self.waiting_dialog(wallet.synchronize)
+ else:
+ r = run_hook('install_wizard_action', self, wallet, action)
+ if not r:
+ raise BaseException('unknown wizard action', action)
- if action == 'create_2of3_2':
- xpub_hot = wallet.master_public_keys.get("m/")
- r = self.multi_mpk_dialog(xpub_hot, 2)
- if not r:
- return
- xpub1, xpub2 = r
- wallet.add_master_public_key("cold/", xpub1)
- wallet.add_master_public_key("remote/", xpub2)
- wallet.create_account()
- self.waiting_dialog(wallet.synchronize)
+ # next action
+ action = wallet.get_action()
- if action == 'create_2fa_3':
- run_hook('create_remote_key', wallet, self)
- if not wallet.master_public_keys.get("remote/"):
- return
- wallet.create_account()
- self.waiting_dialog(wallet.synchronize)
+ if self.network:
+ if self.network.interfaces:
+ self.network_dialog()
+ else:
+ QMessageBox.information(None, _('Warning'), _('You are offline'), _('OK'))
+ self.network.stop()
+ self.network = None
+ # start wallet threads
+ wallet.start_threads(self.network)
if action == 'restore':
+ self.waiting_dialog(lambda: wallet.restore(self.waiting_label.setText))
+ if self.network:
+ if wallet.is_found():
+ QMessageBox.information(None, _('Information'), _("Recovery successful"), _('OK'))
+ else:
+ QMessageBox.information(None, _('Information'), _("No transactions found for this seed"), _('OK'))
+ else:
+ QMessageBox.information(None, _('Information'), _("This wallet was restored offline. It may contain more addresses than displayed."), _('OK'))
+
+ return wallet
+
+
+
+ def restore(self, t):
if t == 'standard':
text = self.enter_seed_dialog(MSG_ENTER_ANYTHING, None)
@@ -490,18 +487,12 @@ class InstallWizard(QDialog):
else:
raise
- elif t in ['2fa', '2of2']:
+ elif t in ['2of2']:
r = self.multi_seed_dialog(1)
if not r:
return
text1, text2 = r
- password = self.password_dialog()
- if t == '2of2':
- wallet = Wallet_2of2(self.storage)
- elif t == '2of3':
- wallet = Wallet_2of3(self.storage)
- elif t == '2fa':
- wallet = Wallet_2of3(self.storage)
+ wallet = Wallet_2of2(self.storage)
if Wallet.is_seed(text1):
wallet.add_seed(text1, password)
@@ -510,7 +501,7 @@ class InstallWizard(QDialog):
else:
wallet.add_master_public_key("cold/", text2)
- elif Wallet.is_mpk(text1):
+ elif Wallet.is_xpub(text1):
if Wallet.is_seed(text2):
wallet.add_seed(text2, password)
wallet.add_master_public_key("cold/", text1)
@@ -518,10 +509,12 @@ class InstallWizard(QDialog):
wallet.add_master_public_key("m/", text1)
wallet.add_master_public_key("cold/", text2)
- if t == '2fa':
- run_hook('restore_third_key', wallet, self)
+ if wallet.is_watching_only():
+ wallet.create_accounts(None)
+ else:
+ password = self.password_dialog()
+ wallet.create_accounts(password)
- wallet.create_account()
elif t in ['2of3']:
r = self.multi_seed_dialog(2)
@@ -538,7 +531,7 @@ class InstallWizard(QDialog):
else:
wallet.add_master_public_key("cold/", text2)
- elif Wallet.is_mpk(text1):
+ elif Wallet.is_xpub(text1):
if Wallet.is_seed(text2):
wallet.add_seed(text2, password)
wallet.add_master_public_key("cold/", text1)
@@ -546,35 +539,13 @@ class InstallWizard(QDialog):
wallet.add_master_public_key("m/", text1)
wallet.add_master_public_key("cold/", text2)
- wallet.create_account()
+ wallet.create_accounts(password)
else:
raise
+ # create first keys offline
+ self.waiting_dialog(wallet.synchronize)
- #if not self.config.get('server'):
- if self.network:
- if self.network.interfaces:
- self.network_dialog()
- else:
- QMessageBox.information(None, _('Warning'), _('You are offline'), _('OK'))
- self.network.stop()
- self.network = None
-
- # start wallet threads
- wallet.start_threads(self.network)
-
- if action == 'restore':
-
- self.waiting_dialog(lambda: wallet.restore(self.waiting_label.setText))
-
- if self.network:
- if wallet.is_found():
- QMessageBox.information(None, _('Information'), _("Recovery successful"), _('OK'))
- else:
- QMessageBox.information(None, _('Information'), _("No transactions found for this seed"), _('OK'))
- else:
- QMessageBox.information(None, _('Information'), _("This wallet was restored offline. It may contain more addresses than displayed."), _('OK'))
-
- return wallet
+ return wallet
diff --git a/lib/plugins.py b/lib/plugins.py
@@ -34,7 +34,7 @@ def run_hook(name, *args):
global plugins
- found = 0
+ results = []
for p in plugins:
@@ -45,15 +45,15 @@ def run_hook(name, *args):
if not callable(f):
continue
- found += 1
-
try:
- f(*args)
+ r = f(*args)
except Exception:
print_error("Plugin error")
traceback.print_exc(file=sys.stdout)
-
- return found
+
+ results.append((p.name,r))
+
+ return results
diff --git a/lib/wallet.py b/lib/wallet.py
@@ -1257,6 +1257,12 @@ class Deterministic_Wallet(Abstract_Wallet):
return False
return True
+ def get_action(self):
+ if not self.get_master_public_keys():
+ return 'create_seed'
+ if not self.accounts:
+ return 'create_accounts'
+
class NewWallet(Deterministic_Wallet):
@@ -1267,7 +1273,7 @@ class NewWallet(Deterministic_Wallet):
return self.accounts["m/0'"]
def is_watching_only(self):
- return self.master_private_keys is {}
+ return not bool(self.master_private_keys)
def can_create_accounts(self):
return 'm/' in self.master_private_keys.keys()
@@ -1312,7 +1318,8 @@ class NewWallet(Deterministic_Wallet):
def create_accounts(self, password):
# First check the password is valid (this raises if it isn't).
- self.check_password(password)
+ if not self.is_watching_only():
+ self.check_password(password)
self.create_account('Main account', password)
def add_master_public_key(self, name, xpub):
@@ -1419,7 +1426,7 @@ class Wallet_2of2(NewWallet):
def can_import(self):
return False
- def create_account(self):
+ def create_account(self, name, password):
xpub1 = self.master_public_keys.get("m/")
xpub2 = self.master_public_keys.get("cold/")
account = BIP32_Account_2of2({'xpub':xpub1, 'xpub2':xpub2})
@@ -1434,9 +1441,11 @@ class Wallet_2of2(NewWallet):
xpub1 = self.master_public_keys.get("m/")
xpub2 = self.master_public_keys.get("cold/")
if xpub1 is None:
- return 'create_2of2_1'
+ return 'create_seed'
if xpub2 is None:
- return 'create_2of2_2'
+ return 'add_cosigner'
+ if not self.accounts:
+ return 'create_accounts'
class Wallet_2of3(Wallet_2of2):
@@ -1446,7 +1455,7 @@ class Wallet_2of3(Wallet_2of2):
Wallet_2of2.__init__(self, storage)
self.storage.put('wallet_type', '2of3', True)
- def create_account(self):
+ def create_account(self, name, password):
xpub1 = self.master_public_keys.get("m/")
xpub2 = self.master_public_keys.get("cold/")
xpub3 = self.master_public_keys.get("remote/")
@@ -1463,13 +1472,12 @@ class Wallet_2of3(Wallet_2of2):
xpub1 = self.master_public_keys.get("m/")
xpub2 = self.master_public_keys.get("cold/")
xpub3 = self.master_public_keys.get("remote/")
- # fixme: we use order of creation
- if xpub2 and xpub1 is None:
- return 'create_2fa_2'
if xpub1 is None:
- return 'create_2of3_1'
+ return 'create_seed'
if xpub2 is None or xpub3 is None:
- return 'create_2of3_2'
+ return 'add_two_cosigners'
+ if not self.accounts:
+ return 'create_accounts'
class OldWallet(Deterministic_Wallet):
@@ -1550,20 +1558,18 @@ class Wallet(object):
def __new__(self, storage):
config = storage.config
- if config.get('bitkey', False):
- # if user requested support for Bitkey device,
- # import Bitkey driver
- from wallet_bitkey import WalletBitkey
- return WalletBitkey(config)
-
- if storage.get('wallet_type') == '2of2':
- return Wallet_2of2(storage)
-
- if storage.get('wallet_type') == '2of3':
- return Wallet_2of3(storage)
- if storage.get('wallet_type') == 'imported':
- return Imported_Wallet(storage)
+ self.wallet_types = [
+ ('standard', ("Standard wallet"), OldWallet),
+ ('imported', ("Imported wallet"), Imported_Wallet),
+ ('2of2', ("Multisig wallet (2 of 2)"), Wallet_2of2),
+ ('2of3', ("Multisig wallet (2 of 3)"), Wallet_2of3)
+ ]
+ run_hook('add_wallet_types', self.wallet_types)
+
+ for t, l, WalletClass in self.wallet_types:
+ if t == storage.get('wallet_type'):
+ return WalletClass(storage)
if not storage.file_exists:
seed_version = NEW_SEED_VERSION if config.get('bip32') is True else OLD_SEED_VERSION
diff --git a/lib/wallet_bitkey.py b/lib/wallet_bitkey.py
@@ -1,28 +0,0 @@
-#!/usr/bin/env python
-#
-# Electrum - lightweight Bitcoin client
-# Copyright (C) 2011 thomasv@gitorious
-#
-# 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 os
-
-from wallet import Wallet
-#import bitkeylib.bitkey_pb2 as proto
-
-from version import ELECTRUM_VERSION
-SEED_VERSION = 4 # Version of bitkey algorithm
-
-class WalletBitkey(Wallet):
- pass