URI: 
       tMerge pull request #4143 from SomberNight/wallet_file_exception - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 35ae2a0064b5548b5c337280a2763f3534ca569f
   DIR parent 5fef1e7980e6c9811448ad7d9fb6afa4460ac7fc
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Mon, 19 Mar 2018 11:57:24 +0100
       
       Merge pull request #4143 from SomberNight/wallet_file_exception
       
       Catch wallet file related exceptions in Qt wizard
       Diffstat:
         M gui/qt/__init__.py                  |      16 ++++++++++++----
         M lib/bitcoin.py                      |      13 ++++++++-----
         M lib/keystore.py                     |      20 ++++++++++++--------
         M lib/storage.py                      |      26 ++++++++++++++------------
         M lib/util.py                         |       6 ++++++
         M lib/wallet.py                       |      18 ++++++++++--------
         M plugins/trustedcoin/trustedcoin.py  |       2 +-
       
       7 files changed, 63 insertions(+), 38 deletions(-)
       ---
   DIR diff --git a/gui/qt/__init__.py b/gui/qt/__init__.py
       t@@ -44,7 +44,8 @@ from electrum import WalletStorage
        # from electrum.synchronizer import Synchronizer
        # from electrum.verifier import SPV
        # from electrum.util import DebugMem
       -from electrum.util import UserCancelled, print_error
       +from electrum.util import (UserCancelled, print_error,
       +                           WalletFileException, BitcoinException)
        # from electrum.wallet import Abstract_Wallet
        
        from .installwizard import InstallWizard, GoBack
       t@@ -191,7 +192,7 @@ class ElectrumGui:
                except BaseException as e:
                    traceback.print_exc(file=sys.stdout)
                    d = QMessageBox(QMessageBox.Warning, _('Error'),
       -                            _('Cannot load wallet:') + '\n' + str(e))
       +                            _('Cannot load wallet') + ' (1):\n' + str(e))
                    d.exec_()
                    return
                if not wallet:
       t@@ -203,7 +204,14 @@ class ElectrumGui:
                        pass
                    except GoBack as e:
                        print_error('[start_new_window] Exception caught (GoBack)', e)
       -            wizard.terminate()
       +            except (WalletFileException, BitcoinException) as e:
       +                traceback.print_exc(file=sys.stderr)
       +                d = QMessageBox(QMessageBox.Warning, _('Error'),
       +                                _('Cannot load wallet') + ' (2):\n' + str(e))
       +                d.exec_()
       +                return
       +            finally:
       +                wizard.terminate()
                    if not wallet:
                        return
        
       t@@ -220,7 +228,7 @@ class ElectrumGui:
                except BaseException as e:
                    traceback.print_exc(file=sys.stdout)
                    d = QMessageBox(QMessageBox.Warning, _('Error'),
       -                            _('Cannot create window for wallet:') + '\n' + str(e))
       +                            _('Cannot create window for wallet') + ':\n' + str(e))
                    d.exec_()
                    return
                if uri:
   DIR diff --git a/lib/bitcoin.py b/lib/bitcoin.py
       t@@ -32,7 +32,7 @@ import json
        import ecdsa
        import pyaes
        
       -from .util import bfh, bh2u, to_string
       +from .util import bfh, bh2u, to_string, BitcoinException
        from . import version
        from .util import print_error, InvalidPassword, assert_bytes, to_bytes, inv_dict
        from . import segwit_addr
       t@@ -349,7 +349,7 @@ def address_to_script(addr):
                script += push_script(bh2u(hash_160))
                script += '87'                                       # op_equal
            else:
       -        raise BaseException('unknown address type')
       +        raise BitcoinException('unknown address type: {}'.format(addrtype))
            return script
        
        def address_to_scripthash(addr):
       t@@ -493,7 +493,8 @@ def deserialize_privkey(key):
                vch = DecodeBase58Check(key)
            except BaseException:
                neutered_privkey = str(key)[:3] + '..' + str(key)[-2:]
       -        raise BaseException("cannot deserialize", neutered_privkey)
       +        raise BitcoinException("cannot deserialize privkey {}"
       +                               .format(neutered_privkey))
        
            if txin_type is None:
                # keys exported in version 3.0.x encoded script type in first byte
       t@@ -888,7 +889,8 @@ def deserialize_xkey(xkey, prv, *, net=None):
                net = constants.net
            xkey = DecodeBase58Check(xkey)
            if len(xkey) != 78:
       -        raise BaseException('Invalid length')
       +        raise BitcoinException('Invalid length for extended key: {}'
       +                               .format(len(xkey)))
            depth = xkey[4]
            fingerprint = xkey[5:9]
            child_number = xkey[9:13]
       t@@ -896,7 +898,8 @@ def deserialize_xkey(xkey, prv, *, net=None):
            header = int('0x' + bh2u(xkey[0:4]), 16)
            headers = net.XPRV_HEADERS if prv else net.XPUB_HEADERS
            if header not in headers.values():
       -        raise BaseException('Invalid xpub format', hex(header))
       +        raise BitcoinException('Invalid extended key format: {}'
       +                               .format(hex(header)))
            xtype = list(headers.keys())[list(headers.values()).index(header)]
            n = 33 if prv else 32
            K_or_k = xkey[13+n:]
   DIR diff --git a/lib/keystore.py b/lib/keystore.py
       t@@ -29,7 +29,8 @@ from unicodedata import normalize
        from . import bitcoin
        from .bitcoin import *
        from . import constants
       -from .util import PrintError, InvalidPassword, hfu
       +from .util import (PrintError, InvalidPassword, hfu, WalletFileException,
       +                   BitcoinException)
        from .mnemonic import Mnemonic, load_wordlist
        from .plugins import run_hook
        
       t@@ -615,7 +616,8 @@ def xpubkey_to_address(x_pubkey):
                mpk, s = Old_KeyStore.parse_xpubkey(x_pubkey)
                pubkey = Old_KeyStore.get_pubkey_from_mpk(mpk, s[0], s[1])
            else:
       -        raise BaseException("Cannot parse pubkey")
       +        raise BitcoinException("Cannot parse pubkey. prefix: {}"
       +                               .format(x_pubkey[0:2]))
            if pubkey:
                address = public_key_to_p2pkh(bfh(pubkey))
            return pubkey, address
       t@@ -634,14 +636,15 @@ def hardware_keystore(d):
            if hw_type in hw_keystores:
                constructor = hw_keystores[hw_type]
                return constructor(d)
       -    raise BaseException('unknown hardware type', hw_type)
       +    raise WalletFileException('unknown hardware type: {}'.format(hw_type))
        
        def load_keystore(storage, name):
       -    w = storage.get('wallet_type', 'standard')
            d = storage.get(name, {})
            t = d.get('type')
            if not t:
       -        raise BaseException('wallet format requires update')
       +        raise WalletFileException(
       +            'Wallet format requires update.\n'
       +            'Cannot find keystore for name {}'.format(name))
            if t == 'old':
                k = Old_KeyStore(d)
            elif t == 'imported':
       t@@ -651,7 +654,8 @@ def load_keystore(storage, name):
            elif t == 'hardware':
                k = hardware_keystore(d)
            else:
       -        raise BaseException('unknown wallet type', t)
       +        raise WalletFileException(
       +            'Unknown type {} for keystore named {}'.format(t, name))
            return k
        
        
       t@@ -709,7 +713,7 @@ def from_seed(seed, passphrase, is_p2sh):
                    xtype = 'p2wsh' if is_p2sh else 'p2wpkh'
                keystore.add_xprv_from_seed(bip32_seed, xtype, der)
            else:
       -        raise BaseException(t)
       +        raise BitcoinException('Unexpected seed type {}'.format(t))
            return keystore
        
        def from_private_key_list(text):
       t@@ -743,5 +747,5 @@ def from_master_key(text):
            elif is_xpub(text):
                k = from_xpub(text)
            else:
       -        raise BaseException('Invalid key')
       +        raise BitcoinException('Invalid master key')
            return k
   DIR diff --git a/lib/storage.py b/lib/storage.py
       t@@ -33,7 +33,7 @@ import pbkdf2, hmac, hashlib
        import base64
        import zlib
        
       -from .util import PrintError, profiler, InvalidPassword
       +from .util import PrintError, profiler, InvalidPassword, WalletFileException
        from .plugins import run_hook, plugin_loaders
        from .keystore import bip44_derivation
        from . import bitcoin
       t@@ -113,7 +113,7 @@ class WalletStorage(PrintError):
        
                if not self.manual_upgrades:
                    if self.requires_split():
       -                raise BaseException("This wallet has multiple accounts and must be split")
       +                raise WalletFileException("This wallet has multiple accounts and must be split")
                    if self.requires_upgrade():
                        self.upgrade()
        
       t@@ -174,7 +174,7 @@ class WalletStorage(PrintError):
                elif v == STO_EV_XPUB_PW:
                    return b'BIE2'
                else:
       -            raise Exception('no encryption magic for version: %s' % v)
       +            raise WalletFileException('no encryption magic for version: %s' % v)
        
            def decrypt(self, password):
                ec_key = self.get_key(password)
       t@@ -320,7 +320,7 @@ class WalletStorage(PrintError):
                        storage2.write()
                        result.append(new_path)
                else:
       -            raise BaseException("This wallet has multiple accounts and must be split")
       +            raise WalletFileException("This wallet has multiple accounts and must be split")
                return result
        
            def requires_upgrade(self):
       t@@ -419,7 +419,7 @@ class WalletStorage(PrintError):
                            d['seed'] = seed
                        self.put(key, d)
                else:
       -            raise Exception('Unable to tell wallet type. Is this even a wallet file?')
       +            raise WalletFileException('Unable to tell wallet type. Is this even a wallet file?')
                # remove junk
                self.put('master_public_key', None)
                self.put('master_public_keys', None)
       t@@ -543,7 +543,7 @@ class WalletStorage(PrintError):
                    else:
                        addresses.append(addr)
                if addresses and keypairs:
       -            raise BaseException('mixed addresses and privkeys')
       +            raise WalletFileException('mixed addresses and privkeys')
                elif addresses:
                    self.put('addresses', addresses)
                    self.put('accounts', None)
       t@@ -553,7 +553,7 @@ class WalletStorage(PrintError):
                    self.put('keypairs', keypairs)
                    self.put('accounts', None)
                else:
       -            raise BaseException('no addresses or privkeys')
       +            raise WalletFileException('no addresses or privkeys')
        
            def convert_account(self):
                if not self._is_upgrade_method_needed(0, 13):
       t@@ -566,9 +566,9 @@ class WalletStorage(PrintError):
                if cur_version > max_version:
                    return False
                elif cur_version < min_version:
       -            raise BaseException(
       -                ('storage upgrade: unexpected version %d (should be %d-%d)'
       -                 % (cur_version, min_version, max_version)))
       +            raise WalletFileException(
       +                'storage upgrade: unexpected version {} (should be {}-{})'
       +                .format(cur_version, min_version, max_version))
                else:
                    return True
        
       t@@ -584,7 +584,9 @@ class WalletStorage(PrintError):
                if not seed_version:
                    seed_version = OLD_SEED_VERSION if len(self.get('master_public_key','')) == 128 else NEW_SEED_VERSION
                if seed_version > FINAL_SEED_VERSION:
       -            raise BaseException('This version of Electrum is too old to open this wallet')
       +            raise WalletFileException('This version of Electrum is too old to open this wallet.\n'
       +                                      '(highest supported storage version: {}, version of this file: {})'
       +                                      .format(FINAL_SEED_VERSION, seed_version))
                if seed_version==14 and self.get('seed_type') == 'segwit':
                    self.raise_unsupported_version(seed_version)
                if seed_version >=12:
       t@@ -607,4 +609,4 @@ class WalletStorage(PrintError):
                    else:
                        # creation was complete if electrum was run from source
                        msg += "\nPlease open this file with Electrum 1.9.8, and move your coins to a new wallet."
       -        raise BaseException(msg)
       +        raise WalletFileException(msg)
   DIR diff --git a/lib/util.py b/lib/util.py
       t@@ -84,6 +84,12 @@ class TimeoutException(Exception):
                return self.message
        
        
       +class WalletFileException(Exception): pass
       +
       +
       +class BitcoinException(Exception): pass
       +
       +
        # Throw this exception to unwind the stack like when an error occurs.
        # However unlike other exceptions the user won't be informed.
        class UserCancelled(Exception):
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -44,7 +44,8 @@ import sys
        
        from .i18n import _
        from .util import (NotEnoughFunds, PrintError, UserCancelled, profiler,
       -                   format_satoshis, NoDynamicFeeEstimates, TimeoutException)
       +                   format_satoshis, NoDynamicFeeEstimates, TimeoutException,
       +                   WalletFileException, BitcoinException)
        
        from .bitcoin import *
        from .version import *
       t@@ -131,6 +132,7 @@ def sweep_preparations(privkeys, network, imax=100):
                    find_utxos_for_privkey('p2pk', privkey, compressed)
            if not inputs:
                raise BaseException(_('No inputs found. (Note that inputs need to be confirmed)'))
       +        # FIXME actually inputs need not be confirmed now, see https://github.com/kyuupichan/electrumx/issues/365
            return inputs, keypairs
        
        
       t@@ -335,7 +337,7 @@ class Abstract_Wallet(PrintError):
                addrs = self.get_receiving_addresses()
                if len(addrs) > 0:
                    if not bitcoin.is_address(addrs[0]):
       -                raise Exception('The addresses in this wallet are not bitcoin addresses.')
       +                raise WalletFileException('The addresses in this wallet are not bitcoin addresses.')
        
            def synchronize(self):
                pass
       t@@ -1166,7 +1168,7 @@ class Abstract_Wallet(PrintError):
                    _type, data, value = o
                    if _type == TYPE_ADDRESS:
                        if not is_address(data):
       -                    raise BaseException("Invalid bitcoin address:" + data)
       +                    raise BaseException("Invalid bitcoin address: {}".format(data))
                    if value == '!':
                        if i_max is not None:
                            raise BaseException("More than one output set to spend max")
       t@@ -1339,7 +1341,7 @@ class Abstract_Wallet(PrintError):
        
            def bump_fee(self, tx, delta):
                if tx.is_final():
       -            raise BaseException(_("Cannot bump fee: transaction is final"))
       +            raise BaseException(_('Cannot bump fee') + ': ' + _('transaction is final'))
                inputs = copy.deepcopy(tx.inputs())
                outputs = copy.deepcopy(tx.outputs())
                for txin in inputs:
       t@@ -1370,7 +1372,7 @@ class Abstract_Wallet(PrintError):
                        if delta > 0:
                            continue
                if delta > 0:
       -            raise BaseException(_('Cannot bump fee: could not find suitable outputs'))
       +            raise BaseException(_('Cannot bump fee') + ': ' + _('could not find suitable outputs'))
                locktime = self.get_local_height()
                tx_new = Transaction.from_io(inputs, outputs, locktime=locktime)
                tx_new.BIP_LI01_sort()
       t@@ -1944,14 +1946,14 @@ class Imported_Wallet(Simple_Wallet):
                    txin_type, pubkey = self.keystore.import_privkey(sec, pw)
                except Exception:
                    neutered_privkey = str(sec)[:3] + '..' + str(sec)[-2:]
       -            raise BaseException('Invalid private key', neutered_privkey)
       +            raise BitcoinException('Invalid private key: {}'.format(neutered_privkey))
                if txin_type in ['p2pkh', 'p2wpkh', 'p2wpkh-p2sh']:
                    if redeem_script is not None:
       -                raise BaseException('Cannot use redeem script with', txin_type)
       +                raise BitcoinException('Cannot use redeem script with script type {}'.format(txin_type))
                    addr = bitcoin.pubkey_to_address(txin_type, pubkey)
                elif txin_type in ['p2sh', 'p2wsh', 'p2wsh-p2sh']:
                    if redeem_script is None:
       -                raise BaseException('Redeem script required for', txin_type)
       +                raise BitcoinException('Redeem script required for script type {}'.format(txin_type))
                    addr = bitcoin.redeem_script_to_address(txin_type, redeem_script)
                else:
                    raise NotImplementedError(txin_type)
   DIR diff --git a/plugins/trustedcoin/trustedcoin.py b/plugins/trustedcoin/trustedcoin.py
       t@@ -406,7 +406,7 @@ class TrustedCoinPlugin(BasePlugin):
                    xprv1, xpub1 = self.get_xkeys(seed, passphrase, "m/0'/")
                    xprv2, xpub2 = self.get_xkeys(seed, passphrase, "m/1'/")
                else:
       -            raise BaseException('unrecognized seed length')
       +            raise BaseException('unrecognized seed length: {} words'.format(n))
                return xprv1, xpub1, xprv2, xpub2
        
            def create_keystore(self, wizard, seed, passphrase):