better integration of plugins in installwizard (for 2fa, trezor plugins)
authorThomasV <thomasv@gitorious>
Tue, 1 Jul 2014 16:46:11 +0000 (18:46 +0200)
committerThomasV <thomasv@gitorious>
Tue, 1 Jul 2014 16:46:11 +0000 (18:46 +0200)
gui/qt/installwizard.py
lib/plugins.py
lib/wallet.py
lib/wallet_bitkey.py [deleted file]

index e379978..b84cd84 100644 (file)
@@ -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
index 700dbc2..ff4a246 100644 (file)
@@ -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
 
 
 
index 2a9c237..f4cc3e7 100644 (file)
@@ -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
deleted file mode 100644 (file)
index e8adf7d..0000000
+++ /dev/null
@@ -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