URI: 
       tbetter integration of plugins in installwizard (for 2fa, trezor plugins) - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 2c38e85a5032e82fd666ecb00eab4d7d5193982e
   DIR parent 101fe08ac8270631e484ab06785d663d3f872673
  HTML Author: ThomasV <thomasv@gitorious>
       Date:   Tue,  1 Jul 2014 18:46:11 +0200
       
       better integration of plugins in installwizard (for 2fa, trezor plugins)
       
       Diffstat:
         M gui/qt/installwizard.py             |     257 ++++++++++++++-----------------
         M lib/plugins.py                      |      12 ++++++------
         M lib/wallet.py                       |      54 +++++++++++++++++--------------
         D lib/wallet_bitkey.py                |      28 ----------------------------
       
       4 files changed, 150 insertions(+), 201 deletions(-)
       ---
   DIR diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py
       t@@ -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
       t@@ -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)
       t@@ -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):
       t@@ -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)
       t@@ -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)
       t@@ -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)
       t@@ -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)
       t@@ -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)
       t@@ -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)
       t@@ -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)
       t@@ -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
   DIR diff --git a/lib/plugins.py b/lib/plugins.py
       t@@ -34,7 +34,7 @@ def run_hook(name, *args):
            
            global plugins
        
       -    found = 0
       +    results = []
        
            for p in plugins:
        
       t@@ -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
        
        
        
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -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):
        
       t@@ -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()
       t@@ -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):
       t@@ -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})
       t@@ -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):
       t@@ -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/")
       t@@ -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):
       t@@ -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
   DIR diff --git a/lib/wallet_bitkey.py b/lib/wallet_bitkey.py
       t@@ -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