From e47e0afa9165bb0dc4395d40d3aa32ddbb21cf25 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Mon, 11 Apr 2022 17:05:26 +0200 Subject: [PATCH] commands: add "version_info" cmd example: ``` $ ./run_electrum -o version_info { "aiohttp.version": "3.8.1", "aiorpcx.version": "0.22.1", "certifi.version": "2021.10.08", "cryptodome.version": null, "cryptography.path": "/home/user/.local/lib/python3.8/site-packages/cryptography", "cryptography.version": "3.4.6", "dnspython.version": "2.2.0", "electrum.path": "/home/user/wspace/electrum/electrum", "electrum.version": "4.2.1", "hidapi.version": "0.11.0.post2", "libsecp256k1.path": "/home/user/wspace/electrum/electrum/libsecp256k1.so.0", "libusb.path": "libusb-1.0.so", "libusb.version": "1.0.23.11397", "libzbar.path": "/home/user/wspace/electrum/electrum/libzbar.so.0", "pyaes.version": "1.3.0", "pyqt.path": "/usr/lib/python3/dist-packages/PyQt5", "pyqt.version": "5.14.1", "qt.version": "5.12.8" } ``` --- electrum/commands.py | 43 +++++++++++++++++++++++++++++++++-- electrum/crypto.py | 23 ++++++++++++++++++- electrum/ecc_fast.py | 6 +++++ electrum/gui/__init__.py | 6 ++++- electrum/gui/kivy/__init__.py | 9 ++++++++ electrum/gui/qt/__init__.py | 10 ++++++++ electrum/plugin.py | 24 ++++++++++++++++++- electrum/qrscanner.py | 6 +++++ 8 files changed, 122 insertions(+), 5 deletions(-) diff --git a/electrum/commands.py b/electrum/commands.py index 4b1abc77a..edd971616 100644 --- a/electrum/commands.py +++ b/electrum/commands.py @@ -38,6 +38,7 @@ from functools import wraps, partial from itertools import repeat from decimal import Decimal from typing import Optional, TYPE_CHECKING, Dict, List +import os from .import util, ecc from .util import (bfh, bh2u, format_satoshis, json_decode, json_normalize, @@ -57,11 +58,13 @@ from .lnutil import SENT, RECEIVED from .lnutil import LnFeatures from .lnutil import extract_nodeid from .lnpeer import channel_id_from_funding_tx -from .plugin import run_hook +from .plugin import run_hook, DeviceMgr from .version import ELECTRUM_VERSION from .simple_config import SimpleConfig from .invoices import LNInvoice from . import submarine_swaps +from . import GuiImportError +from . import crypto if TYPE_CHECKING: @@ -546,9 +549,45 @@ class Commands: @command('') async def version(self): """Return the version of Electrum.""" - from .version import ELECTRUM_VERSION return ELECTRUM_VERSION + @command('') + async def version_info(self): + """Return information about dependencies, such as their version and path.""" + ret = { + "electrum.version": ELECTRUM_VERSION, + "electrum.path": os.path.dirname(os.path.realpath(__file__)), + } + # add currently running GUI + if self.daemon and self.daemon.gui_object: + ret.update(self.daemon.gui_object.version_info()) + # always add Qt GUI, so we get info even when running this from CLI + try: + from .gui.qt import ElectrumGui as QtElectrumGui + ret.update(QtElectrumGui.version_info()) + except GuiImportError: + pass + # Add shared libs (.so/.dll), and non-pure-python dependencies. + # Such deps can be installed in various ways - often via the Linux distro's pkg manager, + # instead of using pip, hence it is useful to list them for debugging. + from . import ecc_fast + ret.update(ecc_fast.version_info()) + from . import qrscanner + ret.update(qrscanner.version_info()) + ret.update(DeviceMgr.version_info()) + ret.update(crypto.version_info()) + # add some special cases + import aiohttp + ret["aiohttp.version"] = aiohttp.__version__ + import aiorpcx + ret["aiorpcx.version"] = aiorpcx._version_str + import certifi + ret["certifi.version"] = certifi.__version__ + import dns + ret["dnspython.version"] = dns.__version__ + + return ret + @command('w') async def getmpk(self, wallet: Abstract_Wallet = None): """Get master public key. Return your wallet\'s master public key""" diff --git a/electrum/crypto.py b/electrum/crypto.py index 8fc02a8d8..fd255c8f0 100644 --- a/electrum/crypto.py +++ b/electrum/crypto.py @@ -28,7 +28,7 @@ import os import sys import hashlib import hmac -from typing import Union +from typing import Union, Mapping, Optional from .util import assert_bytes, InvalidPassword, to_bytes, to_string, WalletFileException, versiontuple from .i18n import _ @@ -84,6 +84,27 @@ if not (HAS_CRYPTODOME or HAS_CRYPTOGRAPHY): sys.exit(f"Error: at least one of ('pycryptodomex', 'cryptography') needs to be installed.") +def version_info() -> Mapping[str, Optional[str]]: + ret = {} + if HAS_PYAES: + ret["pyaes.version"] = ".".join(map(str, pyaes.VERSION[:3])) + else: + ret["pyaes.version"] = None + if HAS_CRYPTODOME: + ret["cryptodome.version"] = Cryptodome.__version__ + if hasattr(Cryptodome, "__path__"): + ret["cryptodome.path"] = ", ".join(Cryptodome.__path__ or []) + else: + ret["cryptodome.version"] = None + if HAS_CRYPTOGRAPHY: + ret["cryptography.version"] = cryptography.__version__ + if hasattr(cryptography, "__path__"): + ret["cryptography.path"] = ", ".join(cryptography.__path__ or []) + else: + ret["cryptography.version"] = None + return ret + + class InvalidPadding(Exception): pass diff --git a/electrum/ecc_fast.py b/electrum/ecc_fast.py index 3a6e706e4..d95234aa0 100644 --- a/electrum/ecc_fast.py +++ b/electrum/ecc_fast.py @@ -138,3 +138,9 @@ except BaseException as e: if _libsecp256k1 is None: # hard fail: sys.exit(f"Error: Failed to load libsecp256k1.") + + +def version_info() -> dict: + return { + "libsecp256k1.path": _libsecp256k1._name if _libsecp256k1 else None, + } diff --git a/electrum/gui/__init__.py b/electrum/gui/__init__.py index 4d4ccdcdd..4d366879c 100644 --- a/electrum/gui/__init__.py +++ b/electrum/gui/__init__.py @@ -4,7 +4,7 @@ # Notifications about network events are sent to the GUI by using network.register_callback() -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Mapping, Optional if TYPE_CHECKING: from . import qt @@ -28,3 +28,7 @@ class BaseElectrumGui: This method must be thread-safe. """ pass + + @classmethod + def version_info(cls) -> Mapping[str, Optional[str]]: + return {} diff --git a/electrum/gui/kivy/__init__.py b/electrum/gui/kivy/__init__.py index 553037673..b7c9249a6 100644 --- a/electrum/gui/kivy/__init__.py +++ b/electrum/gui/kivy/__init__.py @@ -80,3 +80,12 @@ class ElectrumGui(BaseElectrumGui, Logger): if not app: return Clock.schedule_once(lambda dt: app.stop()) + + @classmethod + def version_info(cls): + ret = { + "kivy.version": kivy.__version__, + } + if hasattr(kivy, "__path__"): + ret["kivy.path"] = ", ".join(kivy.__path__ or []) + return ret diff --git a/electrum/gui/qt/__init__.py b/electrum/gui/qt/__init__.py index 76774fa8d..9ff817b8f 100644 --- a/electrum/gui/qt/__init__.py +++ b/electrum/gui/qt/__init__.py @@ -472,3 +472,13 @@ class ElectrumGui(BaseElectrumGui, Logger): def stop(self): self.logger.info('closing GUI') self.app.quit_signal.emit() + + @classmethod + def version_info(cls): + ret = { + "qt.version": QtCore.QT_VERSION_STR, + "pyqt.version": QtCore.PYQT_VERSION_STR, + } + if hasattr(PyQt5, "__path__"): + ret["pyqt.path"] = ", ".join(PyQt5.__path__ or []) + return ret diff --git a/electrum/plugin.py b/electrum/plugin.py index 2d5592d16..fe7b93932 100644 --- a/electrum/plugin.py +++ b/electrum/plugin.py @@ -29,7 +29,7 @@ import time import threading import sys from typing import (NamedTuple, Any, Union, TYPE_CHECKING, Optional, Tuple, - Dict, Iterable, List, Sequence, Callable, TypeVar) + Dict, Iterable, List, Sequence, Callable, TypeVar, Mapping) import concurrent from concurrent import futures from functools import wraps, partial @@ -749,3 +749,25 @@ class DeviceMgr(ThreadJob): client.handler.update_status(False) return devices + + @classmethod + def version_info(cls) -> Mapping[str, Optional[str]]: + ret = {} + # add libusb + try: + import usb1 + except Exception as e: + ret["libusb.version"] = None + else: + ret["libusb.version"] = ".".join(map(str, usb1.getVersion()[:4])) + try: + ret["libusb.path"] = usb1.libusb1.libusb._name + except AttributeError: + ret["libusb.path"] = None + # add hidapi + from importlib.metadata import version + try: + ret["hidapi.version"] = version("hidapi") + except ImportError: + ret["hidapi.version"] = None + return ret diff --git a/electrum/qrscanner.py b/electrum/qrscanner.py index dab4e87de..424dd4186 100644 --- a/electrum/qrscanner.py +++ b/electrum/qrscanner.py @@ -102,5 +102,11 @@ def find_system_cameras() -> Mapping[str, str]: return devices +def version_info() -> Mapping[str, Optional[str]]: + return { + "libzbar.path": libzbar._name if libzbar else None, + } + + if __name__ == "__main__": print(scan_barcode())