diff --git a/MANIFEST.in b/MANIFEST.in index d09f4f5d4..f39e32025 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,5 @@ include LICENCE RELEASE-NOTES AUTHORS include README.rst -include electrum.conf.sample include electrum.desktop include *.py include run_electrum @@ -8,11 +7,15 @@ include contrib/requirements/requirements.txt include contrib/requirements/requirements-hw.txt recursive-include packages *.py recursive-include packages cacert.pem -include icons.qrc -graft icons graft electrum prune electrum/tests +graft contrib/udev + +exclude electrum/*.so +exclude electrum/*.so.0 global-exclude __pycache__ -global-exclude *.py[co] +global-exclude *.py[co~] +global-exclude *.py.orig +global-exclude *.py.rej diff --git a/README.rst b/README.rst index 3f67724b4..2a340dbd4 100644 --- a/README.rst +++ b/README.rst @@ -26,13 +26,30 @@ Electrum - Lightweight Bitcoin client Getting started =============== -Electrum is a pure python application. If you want to use the -Qt interface, install the Qt dependencies:: +Electrum itself is pure Python, and so are most of the required dependencies. + +Non-python dependencies +----------------------- + +If you want to use the Qt interface, install the Qt dependencies:: sudo apt-get install python3-pyqt5 +For elliptic curve operations, libsecp256k1 is a required dependency:: + + sudo apt-get install libsecp256k1-0 + +Alternatively, when running from a cloned repository, a script is provided to build +libsecp256k1 yourself:: + + ./contrib/make_libsecp256k1.sh + + +Running from tar.gz +------------------- + If you downloaded the official package (tar.gz), you can run -Electrum from its root directory, without installing it on your +Electrum from its root directory without installing it on your system; all the python dependencies are included in the 'packages' directory. To run Electrum from its root directory, just do:: @@ -40,40 +57,30 @@ directory. To run Electrum from its root directory, just do:: You can also install Electrum on your system, by running this command:: - sudo apt-get install python3-setuptools - python3 -m pip install .[fast] + sudo apt-get install python3-setuptools python3-pip + python3 -m pip install --user . This will download and install the Python dependencies used by -Electrum, instead of using the 'packages' directory. -The 'fast' extra contains some optional dependencies that we think -are often useful but they are not strictly needed. +Electrum instead of using the 'packages' directory. If you cloned the git repository, you need to compile extra files before you can run Electrum. Read the next section, "Development -Version". - +version". Development version -=================== +------------------- Check out the code from GitHub:: git clone git://github.com/spesmilo/electrum.git cd electrum + git submodule update --init Run install (this should install dependencies):: - python3 -m pip install .[fast] + python3 -m pip install --user . -Render the SVG icons to PNGs (optional):: - - for i in lock unlock confirmed status_lagging status_disconnected status_connected_proxy status_connected status_waiting preferences; do convert -background none icons/$i.svg icons/$i.png; done - -Compile the icons file for Qt:: - - sudo apt-get install pyqt5-dev-tools - pyrcc5 icons.qrc -o electrum/gui/qt/icons_rc.py Compile the protobuf description file:: @@ -83,7 +90,7 @@ Compile the protobuf description file:: Create translations (optional):: sudo apt-get install python-requests gettext - ./contrib/make_locale + ./contrib/pull_locale @@ -91,25 +98,31 @@ Create translations (optional):: Creating Binaries ================= +Linux (tarball) +--------------- -To create binaries, create the 'packages' directory:: +See :code:`contrib/build-linux/README.md`. - ./contrib/make_packages -This directory contains the python dependencies used by Electrum. +Linux (AppImage) +---------------- + +See :code:`contrib/build-linux/appimage/README.md`. + Mac OS X / macOS --------- +---------------- + +See :code:`contrib/osx/README.md`. -See `contrib/build-osx/`. Windows ------- -See `contrib/build-wine/`. +See :code:`contrib/build-wine/README.md`. Android ------- -See `electrum/gui/kivy/Readme.md` file. +See :code:`electrum/gui/kivy/Readme.md`. diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 9350036e3..6d2a050a6 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,3 +1,174 @@ +# Release 4.0 - (Not released yet; release notes are incomplete) + + * Lightning Network + * Qt GUI: Separation between output selection and transaction finalization. + * Http PayServer can be configured from GUI + +# Release 3.3.8 - (July 11, 2019) + + * fix some bugs with recent bump fee (RBF) improvements (#5483, #5502) + * fix #5491: watch-only wallets could not bump fee in some cases + * appimage: URLs could not be opened on some desktop environments (#5425) + * faster tx signing for segwit inputs for really large txns (#5494) + * A few other minor bugfixes and usability improvements. + + +# Release 3.3.7 - (July 3, 2019) + + * The AppImage Linux x86_64 binary and the Windows setup.exe + (so now all Windows binaries) are now built reproducibly. + * Bump fee (RBF) improvements: + Implemented a new fee-bump strategy that can add new inputs, + so now any tx can be fee-bumped (d0a4366). The old strategy + was to decrease the value of outputs (starting with change). + We will now try the new strategy first, and only use the old + as a fallback (needed e.g. when spending "Max"). + * CoinChooser improvements: + - more likely to construct txs without change (when possible) + - less likely to construct txs with really small change (e864fa5) + - will now only spend negative effective value coins when + beneficial for privacy (cb69aa8) + * fix long-standing bug that broke wallets with >65k addresses (#5366) + * Windows binaries: we now build the PyInstaller boot loader ourselves, + as this seems to reduce anti-virus false positives (1d0f679) + * Android: (fix) BIP70 payment requests could not be paid (#5376) + * Android: allow copy-pasting partial transactions from/to clipboard + * Fix a performance regression for large wallets (c6a54f0) + * Qt: fix some high DPI issues related to text fields (37809be) + * Trezor: + - allow bypassing "too old firmware" error (#5391) + - use only the Bridge to scan devices if it is available (#5420) + * hw wallets: (known issue) on Win10-1903, some hw devices + (that also have U2F functionality) can only be detected with + Administrator privileges. (see #5420 and #5437) + A workaround is to run as Admin, or for Trezor to install the Bridge. + * Several other minor bugfixes and usability improvements. + + +# Release 3.3.6 - (May 16, 2019) + + * qt: fix crash during 2FA wallet creation (#5334) + * fix synchronizer not to keep resubscribing to addresses of + already closed wallets (e415c0d9) + * fix removing addresses/keys from imported wallets (#4481) + * kivy: fix crash when aborting 2FA wallet creation (#5333) + * kivy: fix rare crash when changing exchange rate settings (#5329) + * A few other minor bugfixes and usability improvements. + + +# Release 3.3.5 - (May 9, 2019) + + * The logging system has been overhauled (#5296). + Logs can now also optionally be written to disk, disabled by default. + * Fix a bug in synchronizer (#5122) where client could get stuck. + Also, show the progress of history sync in the GUI. (#5319) + * fix Revealer in Windows and MacOS binaries (#5027) + * fiat rate providers: + - added CoinGecko.com and CoinCap.io + - BitcoinAverage now only provides historical exchange rates for + paying customers. Changed default provider to CoinGecko.com (#5188) + * hardware wallets: + - Ledger: Nano X is now recognized (#5140) + - KeepKey: + - device was not getting detected using Windows binary (#5165) + - support firmware 6.0.0+ (#5205) + - Trezor: implemented "seedless" mode (#5118) + * Coin Control in Qt: implemented freezing individual UTXOs + in addition to freezing addresses (#5152) + * TrustedCoin (2FA wallets): + - better error messages (#5184) + - longer signing timeout (#5221) + * Kivy: + - fix bug with local transactions (#5156) + - allow selecting fiat rate providers without historical data (#5162) + * fix CPFP: the fees already paid by the parent were not included in + the calculation, so it always overestimated (#5244) + * Testnet: there is now a warning when the client is started in + testnet mode as there were a number of reports of users getting + scammed through social engineering (#5295) + * CoinChooser: performance of creating transactions has been improved + significantly for large wallets. (d56917f4) + * Importing/sweeping WIF keys: stricter checks (#4638, #5290) + * Electrum protocol: the client's "user agent" has been changed from + "3.3.5" to "electrum/3.3.5". Other libraries connecting to servers + can consider not "spoofing" to be Electrum. (#5246) + * Several other minor bugfixes and usability improvements. + + +# Release 3.3.4 - (February 13, 2019) + + * AppImage: we now also distribute self-contained binaries for x86_64 + Linux in the form of an AppImage (#5042). The Python interpreter, + PyQt5, libsecp256k1, PyCryptodomex, zbar, hidapi/libusb (including + hardware wallet libraries) are all bundled. Note that users of + hw wallets still need to set udev rules themselves. + * hw wallets: fix a regression during transaction signing that prompts + the user too many times for confirmations (commit 2729909) + * transactions now set nVersion to 2, to mimic Bitcoin Core + * fix Qt bug that made all hw wallets unusable on Windows 8.1 (#4960) + * fix bugs in wallet creation wizard that resulted in corrupted + wallets being created in rare cases (#5082, #5057) + * fix compatibility with Qt 5.12 (#5109) + + +# Release 3.3.3 - (January 25, 2019) + + * Do not expose users to server error messages (#4968) + * Notify users of new releases. Release announcements must be signed, + and they are verified byElectrum using a hardcoded Bitcoin address. + * Hardware wallet fixes (#4991, #4993, #5006) + * Display only QR code in QRcode Window + * Fixed code signing on MacOS + * Randomise locktime of transactions + + +# Release 3.3.2 - (December 21, 2018) + + * Fix Qt history export bug + * Improve network timeouts + * Prepend server transaction_broadcast error messages with + explanatory message. Render error messages as plain text. + + +# Release 3.3.1 - (December 20, 2018) + + * Qt: Fix invoices tab crash (#4941) + * Android: Minor GUI improvements + + +# Release 3.3.0 - Hodler's Edition (December 19, 2018) + + * The network layer has been rewritten using asyncio and aiorpcx. + In addition to easier maintenance, this makes the client + more robust against misbehaving servers. + * The minimum python version was increased to 3.6 + * The blockchain headers and fork handling logic has been generalized. + Clients by default now follow chain based on most work, not length. + * New wallet creation defaults to native segwit (bech32). + * Segwit 2FA: TrustedCoin now supports native segwit p2wsh + two-factor wallets. + * RBF batching (opt-in): If the wallet has an unconfirmed RBF + transaction, new payments will be added to that transaction, + instead of creating new transactions. + * MacOS: support QR code scanner in binaries. + * Android APK: + - build using Google NDK instead of Crystax NDK + - target API 28 + - do not use external storage (previously for block headers) + * hardware wallets: + - Coldcard now supports spending from p2wpkh-p2sh, + fixed p2pkh signing for fw 1.1.0 + - Archos Safe-T mini: fix #4726 signing issue + - KeepKey: full segwit support + - Trezor: refactoring and compat with python-trezor 0.11 + - Digital BitBox: support firmware v5.0.0 + * fix bitcoin URI handling when app already running (#4796) + * Qt listings rewritten: + the History tab now uses QAbstractItemModel, the other tabs use + QStandardItemModel. Performance should be better for large wallets. + * Several other minor bugfixes and usability improvements. + + # Release 3.2.3 - (September 3, 2018) * hardware wallet: the Safe-T mini from Archos is now supported. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..9bed5c11f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +## Reporting a Vulnerability + +To report security issues send an email to electrumdev@gmail.com. + +The following keys may be used to communicate sensitive information to developers: + +| Name | Fingerprint | +|------|-------------| +| ThomasV | 6694 D8DE 7BE8 EE56 31BE D950 2BD5 824B 7F94 70E6 | +| SomberNight | 4AD6 4339 DFA0 5E20 B3F6 AD51 E7B7 48CD AF5E 5ED9 | + +You can import a key by running the following command with that +individual’s fingerprint: `gpg --recv-keys ""` +Ensure that you put quotes around fingerprints containing spaces. + +These public keys can also be found in the Electrum git repository, +in the top-level `pubkeys` folder. diff --git a/electrum-env b/electrum-env index 177c69aa2..772131a9d 100755 --- a/electrum-env +++ b/electrum-env @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # This script creates a virtualenv named 'env' and installs all # python dependencies before activating the env and running Electrum. diff --git a/electrum.desktop b/electrum.desktop index 481f3b938..e40106716 100644 --- a/electrum.desktop +++ b/electrum.desktop @@ -10,7 +10,8 @@ Icon=electrum Name[en_US]=Electrum Bitcoin Wallet Name=Electrum Bitcoin Wallet Categories=Finance;Network; -StartupNotify=false +StartupNotify=true +StartupWMClass=electrum Terminal=false Type=Application MimeType=x-scheme-handler/bitcoin; diff --git a/run_electrum b/run_electrum index dbb88d9c1..e7f6a29b9 100755 --- a/run_electrum +++ b/run_electrum @@ -25,12 +25,26 @@ # SOFTWARE. import os import sys +import warnings +import asyncio + +MIN_PYTHON_VERSION = "3.6.1" # FIXME duplicated from setup.py +_min_python_version_tuple = tuple(map(int, (MIN_PYTHON_VERSION.split(".")))) + + +if sys.version_info[:3] < _min_python_version_tuple: + sys.exit("Error: Electrum requires Python version >= %s..." % MIN_PYTHON_VERSION) + script_dir = os.path.dirname(os.path.realpath(__file__)) is_bundle = getattr(sys, 'frozen', False) is_local = not is_bundle and os.path.exists(os.path.join(script_dir, "electrum.desktop")) is_android = 'ANDROID_DATA' in os.environ +if is_local: # running from source + # developers should probably see all deprecation warnings. + warnings.simplefilter('default', DeprecationWarning) + # move this back to gui/kivy/__init.py once plugins are moved os.environ['KIVY_DATA_DIR'] = os.path.abspath(os.path.dirname(__file__)) + '/electrum/gui/kivy/data/' @@ -44,37 +58,41 @@ def check_imports(): import dns import pyaes import ecdsa - import requests + import certifi import qrcode import google.protobuf - import jsonrpclib import aiorpcx except ImportError as e: - sys.exit("Error: %s. Try 'sudo pip install '"%str(e)) + sys.exit(f"Error: {str(e)}. Try 'sudo python3 -m pip install '") # the following imports are for pyinstaller from google.protobuf import descriptor from google.protobuf import message from google.protobuf import reflection from google.protobuf import descriptor_pb2 - from jsonrpclib import SimpleJSONRPCServer # make sure that certificates are here - assert os.path.exists(requests.utils.DEFAULT_CA_BUNDLE_PATH) + assert os.path.exists(certifi.where()) if not is_android: check_imports() +from electrum.logging import get_logger, configure_logging from electrum import util from electrum import constants from electrum import SimpleConfig +from electrum.wallet_db import WalletDB from electrum.wallet import Wallet from electrum.storage import WalletStorage, get_derivation_used_for_hw_device_encryption from electrum.util import print_msg, print_stderr, json_encode, json_decode, UserCancelled -from electrum.util import set_verbosity, InvalidPassword +from electrum.util import InvalidPassword from electrum.commands import get_parser, known_commands, Commands, config_variables from electrum import daemon from electrum import keystore +from electrum.util import create_and_start_event_loop + +_logger = get_logger(__name__) + # get password routine def prompt_password(prompt, confirm=True): @@ -89,31 +107,7 @@ def prompt_password(prompt, confirm=True): return password -def init_daemon(config_options): - config = SimpleConfig(config_options) - storage = WalletStorage(config.get_wallet_path()) - 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) - if storage.is_encrypted(): - if storage.is_encrypted_with_hw_device(): - plugins = init_plugins(config, 'cmdline') - password = get_password_for_hw_device_encrypted_storage(plugins) - elif config.get('password'): - password = config.get('password') - else: - password = prompt_password('Password:', False) - if not password: - print_msg("Error: Password required") - sys.exit(1) - else: - password = None - config_options['password'] = password - - -def init_cmdline(config_options, server): - config = SimpleConfig(config_options) +def init_cmdline(config_options, wallet_path, server): cmdname = config.get('cmd') cmd = known_commands[cmdname] @@ -128,12 +122,12 @@ def init_cmdline(config_options, server): cmd.requires_network = True # instantiate wallet for command-line - storage = WalletStorage(config.get_wallet_path()) + storage = WalletStorage(wallet_path) 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) + sys_exit(1) # important warning if cmd.name in ['getprivatekeys']: @@ -141,9 +135,17 @@ 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.") + # will we need a password + if not storage.is_encrypted(): + db = WalletDB(storage.read(), manual_upgrades=False) + use_encryption = db.get('use_encryption') + else: + use_encryption = True + # 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())): + if ( (cmd.requires_wallet and storage.is_encrypted() and server is False)\ + or (cmdname == 'load_wallet' and storage.is_encrypted())\ + or (cmd.requires_password and use_encryption)): if storage.is_encrypted_with_hw_device(): # this case is handled later in the control flow password = None @@ -153,7 +155,7 @@ def init_cmdline(config_options, server): password = prompt_password('Password:', False) if not password: print_msg("Error: Password required") - sys.exit(1) + sys_exit(1) else: password = None @@ -173,18 +175,18 @@ def get_connected_hw_devices(plugins): name, plugin = splugin.name, splugin.plugin if not plugin: e = splugin.exception - print_stderr(f"{name}: error during plugin init: {repr(e)}") + _logger.error(f"{name}: error during plugin init: {repr(e)}") continue try: u = devmgr.unpaired_device_infos(None, plugin) - except: - devmgr.print_error(f'error getting device infos for {name}: {e}') + except Exception as e: + _logger.error(f'error getting device infos for {name}: {repr(e)}') continue devices += list(map(lambda x: (name, x), u)) return devices -def get_password_for_hw_device_encrypted_storage(plugins): +def get_password_for_hw_device_encrypted_storage(plugins) -> str: devices = get_connected_hw_devices(plugins) if len(devices) == 0: print_msg("Error: No connected hw device found. Cannot decrypt this wallet.") @@ -200,14 +202,16 @@ def get_password_for_hw_device_encrypted_storage(plugins): xpub = plugin.get_xpub(device_info.device.id_, derivation, 'standard', plugin.handler) except UserCancelled: sys.exit(0) - password = keystore.Xpub.get_pubkey_from_xpub(xpub, ()) + password = keystore.Xpub.get_pubkey_from_xpub(xpub, ()).hex() return password -def run_offline_command(config, config_options, plugins): +async def run_offline_command(config, config_options, plugins): cmdname = config.get('cmd') cmd = known_commands[cmdname] password = config_options.get('password') + if 'wallet_path' in cmd.options and config_options.get('wallet_path') is None: + config_options['wallet_path'] = config.get_wallet_path() if cmd.requires_wallet: storage = WalletStorage(config.get_wallet_path()) if storage.is_encrypted(): @@ -215,7 +219,9 @@ def run_offline_command(config, config_options, plugins): password = get_password_for_hw_device_encrypted_storage(plugins) config_options['password'] = password storage.decrypt(password) - wallet = Wallet(storage) + db = WalletDB(storage.read(), manual_upgrades=False) + wallet = Wallet(db, storage, config=config) + config_options['wallet'] = wallet else: wallet = None # check password @@ -235,13 +241,13 @@ def run_offline_command(config, config_options, plugins): # options kwargs = {} for x in cmd.options: - kwargs[x] = (config_options.get(x) if x in ['password', 'new_password'] else config.get(x)) - cmd_runner = Commands(config, wallet, None) + kwargs[x] = (config_options.get(x) if x in ['wallet_path', 'wallet', 'password', 'new_password'] else config.get(x)) + cmd_runner = Commands(config=config) func = getattr(cmd_runner, cmd.name) - result = func(*args, **kwargs) + result = await func(*args, **kwargs) # save wallet if wallet: - wallet.storage.write() + wallet.save_db() return result @@ -249,6 +255,11 @@ def init_plugins(config, gui_name): from electrum.plugin import Plugins return Plugins(config, gui_name) +def sys_exit(i): + # stop event loop and exit + loop.call_soon_threadsafe(stop_loop.set_result, 1) + loop_thread.join(timeout=1) + sys.exit(i) if __name__ == '__main__': # The hook will only be used in the Qt GUI right now @@ -262,6 +273,9 @@ if __name__ == '__main__': sys.argv.append('-h') # old '-v' syntax + # Due to this workaround that keeps old -v working, + # more advanced usages of -v need to use '-v='. + # e.g. -v=debug,network=warning,interface=error try: i = sys.argv.index('-v') except ValueError: @@ -309,8 +323,8 @@ if __name__ == '__main__': if config_options.get('portable'): config_options['electrum_path'] = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'electrum_data') - # kivy sometimes freezes when we write to sys.stderr - set_verbosity(config_options.get('verbosity') if config_options.get('gui') != 'kivy' else '') + if not config_options.get('verbosity'): + warnings.simplefilter('ignore', DeprecationWarning) # check uri uri = config_options.get('url') @@ -318,11 +332,9 @@ if __name__ == '__main__': if not uri.startswith('bitcoin:'): print_stderr('unknown command:', uri) sys.exit(1) - config_options['url'] = uri - # todo: defer this to gui + # singleton config = SimpleConfig(config_options) - cmdname = config.get('cmd') if config.get('testnet'): constants.set_testnet() @@ -331,70 +343,86 @@ if __name__ == '__main__': elif config.get('simnet'): constants.set_simnet() + cmdname = config.get('cmd') + + if cmdname == 'daemon' and config.get("detach"): + # fork before creating the asyncio event loop + pid = os.fork() + if pid: + print_stderr("starting daemon (PID %d)" % pid) + sys.exit(0) + else: + # redirect standard file descriptors + sys.stdout.flush() + sys.stderr.flush() + si = open(os.devnull, 'r') + so = open(os.devnull, 'w') + se = open(os.devnull, 'w') + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) + + loop, stop_loop, loop_thread = create_and_start_event_loop() + if cmdname == 'gui': - fd, server = daemon.get_fd_or_server(config) + configure_logging(config) + fd = daemon.get_file_descriptor(config) if fd is not None: plugins = init_plugins(config, config.get('gui', 'qt')) d = daemon.Daemon(config, fd) - d.init_gui(config, plugins) - sys.exit(0) + d.run_gui(config, plugins) + sys_exit(0) else: - result = server.gui(config_options) + result = daemon.request(config, 'gui', (config_options,)) elif cmdname == 'daemon': - subcommand = config.get('subcommand') - if subcommand in ['load_wallet']: - init_daemon(config_options) - if subcommand in [None, 'start']: - fd, server = daemon.get_fd_or_server(config) - if fd is not None: - if subcommand == 'start': - pid = os.fork() - if pid: - print_stderr("starting daemon (PID %d)" % pid) - sys.exit(0) - init_plugins(config, 'cmdline') - d = daemon.Daemon(config, fd) - if config.get('websocket_server'): - from electrum import websockets - websockets.WebSocketServer(config, d.network) - if config.get('requests_dir'): - path = os.path.join(config.get('requests_dir'), 'index.html') - if not os.path.exists(path): - print("Requests directory not configured.") - print("You can configure it using https://github.com/spesmilo/electrum-merchant") - sys.exit(1) - d.join() - sys.exit(0) - else: - result = server.daemon(config_options) + configure_logging(config) + fd = daemon.get_file_descriptor(config) + if fd is not None: + # run daemon + init_plugins(config, 'cmdline') + d = daemon.Daemon(config, fd) + d.run_daemon() + sys_exit(0) else: - server = daemon.get_server(config) - if server is not None: - result = server.daemon(config_options) - else: - print_msg("Daemon not running") - sys.exit(1) + print_msg("Daemon already running") + sys_exit(1) else: # command line - server = daemon.get_server(config) - init_cmdline(config_options, server) - if server is not None: - result = server.run_cmdline(config_options) + cmd = known_commands[cmdname] + wallet_path = config.get_wallet_path() + if not config.get('offline'): + init_cmdline(config_options, wallet_path, True) + timeout = config.get('timeout', 60) + if timeout: timeout = int(timeout) + try: + result = daemon.request(config, 'run_cmdline', (config_options,), timeout) + except daemon.DaemonNotRunning: + print_msg("Daemon not running; try 'electrum daemon -d'") + if not cmd.requires_network: + print_msg("To run this command without a daemon, use --offline") + sys_exit(1) + except Exception as e: + print_stderr(str(e) or repr(e)) + sys_exit(1) else: - cmd = known_commands[cmdname] if cmd.requires_network: - print_msg("Daemon not running; try 'electrum daemon start'") - sys.exit(1) - else: - plugins = init_plugins(config, 'cmdline') - result = run_offline_command(config, config_options, plugins) - # print result + print_msg("This command cannot be run offline") + sys_exit(1) + init_cmdline(config_options, wallet_path, False) + plugins = init_plugins(config, 'cmdline') + coro = run_offline_command(config, config_options, plugins) + fut = asyncio.run_coroutine_threadsafe(coro, loop) + try: + result = fut.result() + except Exception as e: + print_stderr(str(e) or repr(e)) + sys_exit(1) if isinstance(result, str): print_msg(result) elif type(result) is dict and result.get('error'): print_stderr(result.get('error')) elif result is not None: print_msg(json_encode(result)) - sys.exit(0) + sys_exit(0) diff --git a/setup.py b/setup.py index 3e71caab5..d0ce0fbf3 100755 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ _min_python_version_tuple = tuple(map(int, (MIN_PYTHON_VERSION.split(".")))) if sys.version_info[:3] < _min_python_version_tuple: - sys.exit("Error: Electrum requires Python version >= {}...".format(MIN_PYTHON_VERSION)) + sys.exit("Error: Electrum requires Python version >= %s..." % MIN_PYTHON_VERSION) with open('contrib/requirements/requirements.txt') as f: requirements = f.read().splitlines() @@ -47,34 +47,16 @@ if platform.system() in ['Linux', 'FreeBSD', 'DragonFly']: usr_share = os.path.expanduser('~/.local/share') data_files += [ (os.path.join(usr_share, 'applications/'), ['electrum.desktop']), - (os.path.join(usr_share, icons_dirname), ['icons/electrum.png']) + (os.path.join(usr_share, icons_dirname), ['electrum/gui/icons/electrum.png']), ] extras_require = { 'hardware': requirements_hw, - 'fast': ['pycryptodomex'], 'gui': ['pyqt5'], } extras_require['full'] = [pkg for sublist in list(extras_require.values()) for pkg in sublist] -class CustomInstallCommand(install): - def run(self): - install.run(self) - # potentially build Qt icons file - try: - import PyQt5 - except ImportError: - pass - else: - try: - path = os.path.join(self.install_lib, "electrum/gui/qt/icons_rc.py") - if not os.path.exists(path): - subprocess.call(["pyrcc5", "icons.qrc", "-o", path]) - except Exception as e: - print('Warning: building icons file failed with {}'.format(e)) - - setup( name="Electrum", version=version.ELECTRUM_VERSION, @@ -96,6 +78,9 @@ setup( 'wordlist/*.txt', 'locale/*/LC_MESSAGES/electrum.mo', ], + 'electrum.gui': [ + 'icons/*', + ], }, scripts=['electrum/electrum'], data_files=data_files, @@ -105,7 +90,4 @@ setup( license="MIT Licence", url="https://electrum.org", long_description="""Lightweight Bitcoin Wallet""", - cmdclass={ - 'install': CustomInstallCommand, - }, )