electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 411832c4ce268cab388fac06c019a14940867dab
parent ee2e9f6092d5f173f5744176087dba20794edebe
Author: ThomasV <thomasv@electrum.org>
Date:   Mon,  6 Mar 2017 08:33:35 +0100

cleanup storage and fix tracvis test

Diffstat:
Melectrum | 11+++++------
Mgui/qt/__init__.py | 3+--
Mgui/qt/installwizard.py | 15+++++++--------
Mgui/stdio.py | 7++++---
Mgui/text.py | 5+++--
Mlib/__init__.py | 3++-
Mlib/base_wizard.py | 2+-
Mlib/daemon.py | 9++++-----
Mlib/storage.py | 77+++++++++++++++++++++++++++++++++++------------------------------------------
Mlib/wallet.py | 1-
10 files changed, 62 insertions(+), 71 deletions(-)

diff --git a/electrum b/electrum @@ -124,7 +124,7 @@ def run_non_RPC(config): cmdname = config.get('cmd') storage = WalletStorage(config.get_wallet_path()) - if storage.file_exists: + if storage.file_exists(): sys.exit("Error: Remove the existing wallet first!") def password_dialog(): @@ -187,7 +187,7 @@ def run_non_RPC(config): def init_daemon(config_options): config = SimpleConfig(config_options) storage = WalletStorage(config.get_wallet_path()) - if not storage.file_exists: + if not storage.file_exists(): print_msg("Error: Wallet file not found.") print_msg("Type 'electrum create' to create a new wallet, or provide a path to a wallet with the -w option") sys.exit(0) @@ -222,7 +222,7 @@ def init_cmdline(config_options, server): # instanciate wallet for command-line storage = WalletStorage(config.get_wallet_path()) - if cmd.requires_wallet and not storage.file_exists: + if cmd.requires_wallet and not storage.file_exists(): print_msg("Error: Wallet file not found.") print_msg("Type 'electrum create' to create a new wallet, or provide a path to a wallet with the -w option") sys.exit(0) @@ -233,8 +233,6 @@ def init_cmdline(config_options, server): print_stderr("Exposing a single private key can compromise your entire wallet!") print_stderr("In particular, DO NOT use 'redeem private key' services proposed by third parties.") - if not storage.is_encrypted(): - storage.read(None) # commands needing password if (cmd.requires_wallet and storage.is_encrypted() and server is None)\ or (cmd.requires_password and (storage.get('use_encryption') or storage.is_encrypted())): @@ -263,7 +261,8 @@ def run_offline_command(config, config_options): password = config_options.get('password') if cmd.requires_wallet: storage = WalletStorage(config.get_wallet_path()) - storage.read(password if storage.is_encrypted() else None) + if storage.is_encrypted(): + storage.decrypt(password) wallet = Wallet(storage) else: wallet = None diff --git a/gui/qt/__init__.py b/gui/qt/__init__.py @@ -162,13 +162,12 @@ class ElectrumGui: wallet = self.daemon.get_wallet(path) if not wallet: storage = WalletStorage(path) - if not storage.file_exists or storage.is_encrypted(): + if not storage.file_exists() or storage.is_encrypted(): wizard = InstallWizard(self.config, self.app, self.plugins, storage) wallet = wizard.run_and_get_wallet() if not wallet: return else: - storage.read(None) wallet = Wallet(storage) wallet.start_threads(self.daemon.network) self.daemon.add_wallet(wallet) diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py @@ -6,7 +6,7 @@ from PyQt4.QtCore import * import PyQt4.QtCore as QtCore import electrum -from electrum.wallet import Wallet, WalletStorage +from electrum import Wallet, WalletStorage from electrum.util import UserCancelled, InvalidPassword from electrum.base_wizard import BaseWizard from electrum.i18n import _ @@ -167,12 +167,12 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard): vbox.addLayout(hbox) self.pw_e = None - if not self.storage.file_exists: + if not self.storage.file_exists(): msg = _("This file does not exist.") + '\n' \ + _("Press 'Next' to create this wallet, or chose another file.") vbox.addWidget(QLabel(msg)) - elif self.storage.file_exists and self.storage.is_encrypted(): + elif self.storage.file_exists() and self.storage.is_encrypted(): msg = _("This file is encrypted.") + '\n' + _('Enter your password or choose another file.') vbox.addWidget(QLabel(msg)) hbox2 = QHBoxLayout() @@ -195,20 +195,19 @@ class InstallWizard(QDialog, MessageBoxMixin, BaseWizard): while True: update_layout() - if self.storage.file_exists and not self.storage.is_encrypted(): - self.storage.read(None) + if self.storage.file_exists() and not self.storage.is_encrypted(): break if not self.loop.exec_(): return - if not self.storage.file_exists: + if not self.storage.file_exists(): break - if self.storage.file_exists and self.storage.is_encrypted(): + if self.storage.file_exists() and self.storage.is_encrypted(): password = unicode(self.pw_e.text()) try: - self.storage.read(password) + self.storage.decrypt(password) break except InvalidPassword as e: QMessageBox.information(None, _('Error'), str(e), _('OK')) diff --git a/gui/stdio.py b/gui/stdio.py @@ -1,7 +1,7 @@ from decimal import Decimal _ = lambda x:x #from i18n import _ -from electrum.wallet import WalletStorage, Wallet +from electrum import WalletStorage, Wallet from electrum.util import format_satoshis, set_verbosity, StoreDict from electrum.bitcoin import is_valid, COIN, TYPE_ADDRESS from electrum.network import filter_protocol @@ -19,8 +19,9 @@ class ElectrumGui: if not storage.file_exists: print "Wallet not found. try 'electrum create'" exit() - password = getpass.getpass('Password:', stream=None) if storage.is_encrypted() else None - storage.read(password) + if storage.is_encrypted(): + password = getpass.getpass('Password:', stream=None) + storage.decrypt(password) self.done = 0 self.last_balance = "" diff --git a/gui/text.py b/gui/text.py @@ -22,8 +22,9 @@ class ElectrumGui: if not storage.file_exists: print "Wallet not found. try 'electrum create'" exit() - password = getpass.getpass('Password:', stream=None) if storage.is_encrypted() else None - storage.read(password) + if storage.is_encrypted(): + password = getpass.getpass('Password:', stream=None) + storage.decrypt(password) self.wallet = Wallet(storage) self.wallet.start_threads(self.network) self.contacts = StoreDict(self.config, 'contacts') diff --git a/lib/__init__.py b/lib/__init__.py @@ -1,6 +1,7 @@ from version import ELECTRUM_VERSION from util import format_satoshis, print_msg, print_error, set_verbosity -from wallet import Synchronizer, WalletStorage, Wallet, Imported_Wallet +from wallet import Synchronizer, Wallet, Imported_Wallet +from storage import WalletStorage from coinchooser import COIN_CHOOSERS from network import Network, pick_random_server from interface import Connection, Interface diff --git a/lib/base_wizard.py b/lib/base_wizard.py @@ -26,7 +26,7 @@ import os import bitcoin import keystore -from wallet import Wallet, Imported_Wallet, Standard_Wallet, Multisig_Wallet, WalletStorage, wallet_types +from wallet import Wallet, Imported_Wallet, Standard_Wallet, Multisig_Wallet, wallet_types from i18n import _ from plugins import run_hook diff --git a/lib/daemon.py b/lib/daemon.py @@ -35,7 +35,8 @@ from version import ELECTRUM_VERSION from network import Network from util import json_decode, DaemonThread from util import print_msg, print_error, print_stderr, UserCancelled -from wallet import WalletStorage, Wallet +from wallet import Wallet +from storage import WalletStorage from commands import known_commands, Commands from simple_config import SimpleConfig from plugins import run_hook @@ -203,15 +204,13 @@ class Daemon(DaemonThread): wallet = self.wallets[path] return wallet storage = WalletStorage(path) - if not storage.file_exists: + if not storage.file_exists(): return if storage.is_encrypted(): password = password_getter() if not password: raise UserCancelled() - else: - password = None - storage.read(password) + storage.decrypt(password) if storage.requires_split(): return if storage.requires_upgrade(): diff --git a/lib/storage.py b/lib/storage.py @@ -64,52 +64,19 @@ def multisig_type(wallet_type): class WalletStorage(PrintError): def __init__(self, path): + self.print_error("wallet path", path) self.lock = threading.RLock() self.data = {} self.path = path - self.file_exists = self.path and os.path.exists(self.path) self.modified = False self.pubkey = None - - def decrypt(self, s, password): - # Note: hardware wallets should use a seed-derived key and not require a password. - # Thus, we need to expose keystore metadata - if password is None: - self.pubkey = None - return s - secret = pbkdf2.PBKDF2(password, '', iterations = 1024, macmodule = hmac, digestmodule = hashlib.sha512).read(64) - ec_key = bitcoin.EC_KEY(secret) - self.pubkey = ec_key.get_public_key() - return zlib.decompress(ec_key.decrypt_message(s)) if s else None - - def set_password(self, pw, encrypt): - """Set self.pubkey""" - self.put('use_encryption', bool(pw)) - self.decrypt(None, pw if encrypt else None) - - def is_encrypted(self): - try: + if self.file_exists(): with open(self.path, "r") as f: - s = f.read(8) - except IOError: - return - try: - return base64.b64decode(s).startswith('BIE1') - except: - return False + self.raw = f.read() + if not self.is_encrypted(): + self.load_data(self.raw) - def read(self, password): - """Read the contents of the wallet file.""" - self.print_error("wallet path", self.path) - try: - with open(self.path, "r") as f: - s = f.read() - except IOError: - return - if not s: - return - # Decrypt wallet. - s = self.decrypt(s, password) + def load_data(self, s): try: self.data = json.loads(s) except: @@ -119,6 +86,33 @@ class WalletStorage(PrintError): l = plugin_loaders.get(t) if l: l() + def is_encrypted(self): + try: + return base64.b64decode(self.raw).startswith('BIE1') + except: + return False + + def file_exists(self): + return self.path and os.path.exists(self.path) + + def get_key(self, password): + secret = pbkdf2.PBKDF2(password, '', iterations = 1024, macmodule = hmac, digestmodule = hashlib.sha512).read(64) + ec_key = bitcoin.EC_KEY(secret) + return ec_key + + def decrypt(self, password): + ec_key = self.get_key(password) + s = zlib.decompress(ec_key.decrypt_message(self.raw)) if self.raw else None + self.load_data(s) + + def set_password(self, password, encrypt): + self.put('use_encryption', bool(password)) + if encrypt and password: + ec_key = self.get_key(password) + self.pubkey = ec_key.get_public_key() + else: + self.pubkey = None + def get(self, key, default=None): with self.lock: v = self.data.get(key) @@ -150,7 +144,6 @@ class WalletStorage(PrintError): self.put('seed_version', FINAL_SEED_VERSION) with self.lock: self._write() - self.file_exists = True def _write(self): if threading.currentThread().isDaemon(): @@ -230,7 +223,7 @@ class WalletStorage(PrintError): return result def requires_upgrade(self): - return self.file_exists and self.get_seed_version() != FINAL_SEED_VERSION + return self.file_exists() and self.get_seed_version() != FINAL_SEED_VERSION def upgrade(self): self.convert_imported() @@ -371,7 +364,7 @@ class WalletStorage(PrintError): action = run_hook('get_action', self) if action: return action - if not self.file_exists: + if not self.file_exists(): return 'new' def get_seed_version(self): diff --git a/lib/wallet.py b/lib/wallet.py @@ -63,7 +63,6 @@ from mnemonic import Mnemonic import paymentrequest -from storage import WalletStorage TX_STATUS = [ _('Replaceable'),