commit 35ae2a0064b5548b5c337280a2763f3534ca569f
parent 5fef1e7980e6c9811448ad7d9fb6afa4460ac7fc
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:
7 files changed, 63 insertions(+), 38 deletions(-)
diff --git a/gui/qt/__init__.py b/gui/qt/__init__.py
@@ -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
@@ -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:
@@ -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
@@ -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:
diff --git a/lib/bitcoin.py b/lib/bitcoin.py
@@ -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
@@ -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):
@@ -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
@@ -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]
@@ -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:]
diff --git a/lib/keystore.py b/lib/keystore.py
@@ -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
@@ -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
@@ -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':
@@ -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
@@ -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):
@@ -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
diff --git a/lib/storage.py b/lib/storage.py
@@ -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
@@ -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()
@@ -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)
@@ -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):
@@ -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)
@@ -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)
@@ -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):
@@ -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
@@ -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:
@@ -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)
diff --git a/lib/util.py b/lib/util.py
@@ -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):
diff --git a/lib/wallet.py b/lib/wallet.py
@@ -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 *
@@ -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
@@ -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
@@ -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")
@@ -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:
@@ -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()
@@ -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)
diff --git a/plugins/trustedcoin/trustedcoin.py b/plugins/trustedcoin/trustedcoin.py
@@ -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):