URI: 
       tmv qt update checker to its own file - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 67d080b34a244f6e13c43052a0f4ba3d7c6eb346
   DIR parent 6926b8b2d4fdfa02ab67ad021cd6cce2764bb2fc
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Mon,  4 Feb 2019 18:45:42 +0100
       
       mv qt update checker to its own file
       
       Diffstat:
         M electrum/gui/qt/main_window.py      |       1 +
         A electrum/gui/qt/update_checker.py   |     141 +++++++++++++++++++++++++++++++
         M electrum/gui/qt/util.py             |     134 +------------------------------
       
       3 files changed, 145 insertions(+), 131 deletions(-)
       ---
   DIR diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py
       t@@ -75,6 +75,7 @@ from .fee_slider import FeeSlider
        from .util import *
        from .installwizard import WIF_HELP_TEXT
        from .history_list import HistoryList, HistoryModel
       +from .update_checker import UpdateCheck, UpdateCheckThread
        
        
        class StatusBarButton(QPushButton):
   DIR diff --git a/electrum/gui/qt/update_checker.py b/electrum/gui/qt/update_checker.py
       t@@ -0,0 +1,141 @@
       +# Copyright (C) 2019 The Electrum developers
       +# Distributed under the MIT software license, see the accompanying
       +# file LICENCE or http://www.opensource.org/licenses/mit-license.php
       +
       +import asyncio
       +import base64
       +from distutils.version import StrictVersion
       +
       +from PyQt5.QtCore import Qt, QThread, pyqtSignal
       +from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QLabel, QProgressBar,
       +                             QHBoxLayout, QPushButton)
       +
       +from electrum import version
       +from electrum import constants
       +from electrum import ecc
       +from electrum.i18n import _
       +from electrum.util import PrintError, make_aiohttp_session
       +
       +
       +class UpdateCheck(QWidget, PrintError):
       +    url = "https://electrum.org/version"
       +    download_url = "https://electrum.org/#download"
       +
       +    VERSION_ANNOUNCEMENT_SIGNING_KEYS = (
       +        "13xjmVAB1EATPP8RshTE8S8sNwwSUM9p1P",
       +    )
       +
       +    def __init__(self, main_window, latest_version=None):
       +        self.main_window = main_window
       +        QWidget.__init__(self)
       +        self.setWindowTitle('Electrum - ' + _('Update Check'))
       +        self.content = QVBoxLayout()
       +        self.content.setContentsMargins(*[10]*4)
       +
       +        self.heading_label = QLabel()
       +        self.content.addWidget(self.heading_label)
       +
       +        self.detail_label = QLabel()
       +        self.detail_label.setTextInteractionFlags(Qt.LinksAccessibleByMouse)
       +        self.detail_label.setOpenExternalLinks(True)
       +        self.content.addWidget(self.detail_label)
       +
       +        self.pb = QProgressBar()
       +        self.pb.setMaximum(0)
       +        self.pb.setMinimum(0)
       +        self.content.addWidget(self.pb)
       +
       +        versions = QHBoxLayout()
       +        versions.addWidget(QLabel(_("Current version: {}".format(version.ELECTRUM_VERSION))))
       +        self.latest_version_label = QLabel(_("Latest version: {}".format(" ")))
       +        versions.addWidget(self.latest_version_label)
       +        self.content.addLayout(versions)
       +
       +        self.update_view(latest_version)
       +
       +        self.update_check_thread = UpdateCheckThread(self.main_window)
       +        self.update_check_thread.checked.connect(self.on_version_retrieved)
       +        self.update_check_thread.failed.connect(self.on_retrieval_failed)
       +        self.update_check_thread.start()
       +
       +        close_button = QPushButton(_("Close"))
       +        close_button.clicked.connect(self.close)
       +        self.content.addWidget(close_button)
       +        self.setLayout(self.content)
       +        self.show()
       +
       +    def on_version_retrieved(self, version):
       +        self.update_view(version)
       +
       +    def on_retrieval_failed(self):
       +        self.heading_label.setText('<h2>' + _("Update check failed") + '</h2>')
       +        self.detail_label.setText(_("Sorry, but we were unable to check for updates. Please try again later."))
       +        self.pb.hide()
       +
       +    @staticmethod
       +    def is_newer(latest_version):
       +        return latest_version > StrictVersion(version.ELECTRUM_VERSION)
       +
       +    def update_view(self, latest_version=None):
       +        if latest_version:
       +            self.pb.hide()
       +            self.latest_version_label.setText(_("Latest version: {}".format(latest_version)))
       +            if self.is_newer(latest_version):
       +                self.heading_label.setText('<h2>' + _("There is a new update available") + '</h2>')
       +                url = "<a href='{u}'>{u}</a>".format(u=UpdateCheck.download_url)
       +                self.detail_label.setText(_("You can download the new version from {}.").format(url))
       +            else:
       +                self.heading_label.setText('<h2>' + _("Already up to date") + '</h2>')
       +                self.detail_label.setText(_("You are already on the latest version of Electrum."))
       +        else:
       +            self.heading_label.setText('<h2>' + _("Checking for updates...") + '</h2>')
       +            self.detail_label.setText(_("Please wait while Electrum checks for available updates."))
       +
       +
       +class UpdateCheckThread(QThread, PrintError):
       +    checked = pyqtSignal(object)
       +    failed = pyqtSignal()
       +
       +    def __init__(self, main_window):
       +        super().__init__()
       +        self.main_window = main_window
       +
       +    async def get_update_info(self):
       +        async with make_aiohttp_session(proxy=self.main_window.network.proxy) as session:
       +            async with session.get(UpdateCheck.url) as result:
       +                signed_version_dict = await result.json(content_type=None)
       +                # example signed_version_dict:
       +                # {
       +                #     "version": "3.9.9",
       +                #     "signatures": {
       +                #         "1Lqm1HphuhxKZQEawzPse8gJtgjm9kUKT4": "IA+2QG3xPRn4HAIFdpu9eeaCYC7S5wS/sDxn54LJx6BdUTBpse3ibtfq8C43M7M1VfpGkD5tsdwl5C6IfpZD/gQ="
       +                #     }
       +                # }
       +                version_num = signed_version_dict['version']
       +                sigs = signed_version_dict['signatures']
       +                for address, sig in sigs.items():
       +                    if address not in UpdateCheck.VERSION_ANNOUNCEMENT_SIGNING_KEYS:
       +                        continue
       +                    sig = base64.b64decode(sig)
       +                    msg = version_num.encode('utf-8')
       +                    if ecc.verify_message_with_address(address=address, sig65=sig, message=msg,
       +                                                       net=constants.BitcoinMainnet):
       +                        self.print_error(f"valid sig for version announcement '{version_num}' from address '{address}'")
       +                        break
       +                else:
       +                    raise Exception('no valid signature for version announcement')
       +                return StrictVersion(version_num.strip())
       +
       +    def run(self):
       +        network = self.main_window.network
       +        if not network:
       +            self.failed.emit()
       +            return
       +        try:
       +            update_info = asyncio.run_coroutine_threadsafe(self.get_update_info(), network.asyncio_loop).result()
       +        except Exception as e:
       +            #self.print_error(traceback.format_exc())
       +            self.print_error(f"got exception: '{repr(e)}'")
       +            self.failed.emit()
       +        else:
       +            self.checked.emit(update_info)
   DIR diff --git a/electrum/gui/qt/util.py b/electrum/gui/qt/util.py
       t@@ -5,21 +5,17 @@ import sys
        import platform
        import queue
        import traceback
       -from distutils.version import StrictVersion
       +
        from functools import partial, lru_cache
        from typing import NamedTuple, Callable, Optional, TYPE_CHECKING
       -import base64
        
        from PyQt5.QtGui import *
        from PyQt5.QtCore import *
        from PyQt5.QtWidgets import *
        
       -from electrum import version
       -from electrum import ecc
       -from electrum import constants
        from electrum.i18n import _, languages
       -from electrum.util import (FileImportFailed, FileExportFailed, make_aiohttp_session,
       -                           PrintError, resource_path)
       +from electrum.util import (FileImportFailed, FileExportFailed,
       +                           resource_path)
        from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_EXPIRED
        
        if TYPE_CHECKING:
       t@@ -826,130 +822,6 @@ class FromList(QTreeWidget):
                self.header().setSectionResizeMode(1, sm)
        
        
       -class UpdateCheck(QWidget, PrintError):
       -    url = "https://electrum.org/version"
       -    download_url = "https://electrum.org/#download"
       -
       -    VERSION_ANNOUNCEMENT_SIGNING_KEYS = (
       -        "13xjmVAB1EATPP8RshTE8S8sNwwSUM9p1P",
       -    )
       -
       -    def __init__(self, main_window, latest_version=None):
       -        self.main_window = main_window
       -        QWidget.__init__(self)
       -        self.setWindowTitle('Electrum - ' + _('Update Check'))
       -        self.content = QVBoxLayout()
       -        self.content.setContentsMargins(*[10]*4)
       -
       -        self.heading_label = QLabel()
       -        self.content.addWidget(self.heading_label)
       -
       -        self.detail_label = QLabel()
       -        self.detail_label.setTextInteractionFlags(Qt.LinksAccessibleByMouse)
       -        self.detail_label.setOpenExternalLinks(True)
       -        self.content.addWidget(self.detail_label)
       -
       -        self.pb = QProgressBar()
       -        self.pb.setMaximum(0)
       -        self.pb.setMinimum(0)
       -        self.content.addWidget(self.pb)
       -
       -        versions = QHBoxLayout()
       -        versions.addWidget(QLabel(_("Current version: {}".format(version.ELECTRUM_VERSION))))
       -        self.latest_version_label = QLabel(_("Latest version: {}".format(" ")))
       -        versions.addWidget(self.latest_version_label)
       -        self.content.addLayout(versions)
       -
       -        self.update_view(latest_version)
       -
       -        self.update_check_thread = UpdateCheckThread(self.main_window)
       -        self.update_check_thread.checked.connect(self.on_version_retrieved)
       -        self.update_check_thread.failed.connect(self.on_retrieval_failed)
       -        self.update_check_thread.start()
       -
       -        close_button = QPushButton(_("Close"))
       -        close_button.clicked.connect(self.close)
       -        self.content.addWidget(close_button)
       -        self.setLayout(self.content)
       -        self.show()
       -
       -    def on_version_retrieved(self, version):
       -        self.update_view(version)
       -
       -    def on_retrieval_failed(self):
       -        self.heading_label.setText('<h2>' + _("Update check failed") + '</h2>')
       -        self.detail_label.setText(_("Sorry, but we were unable to check for updates. Please try again later."))
       -        self.pb.hide()
       -
       -    @staticmethod
       -    def is_newer(latest_version):
       -        return latest_version > StrictVersion(version.ELECTRUM_VERSION)
       -
       -    def update_view(self, latest_version=None):
       -        if latest_version:
       -            self.pb.hide()
       -            self.latest_version_label.setText(_("Latest version: {}".format(latest_version)))
       -            if self.is_newer(latest_version):
       -                self.heading_label.setText('<h2>' + _("There is a new update available") + '</h2>')
       -                url = "<a href='{u}'>{u}</a>".format(u=UpdateCheck.download_url)
       -                self.detail_label.setText(_("You can download the new version from {}.").format(url))
       -            else:
       -                self.heading_label.setText('<h2>' + _("Already up to date") + '</h2>')
       -                self.detail_label.setText(_("You are already on the latest version of Electrum."))
       -        else:
       -            self.heading_label.setText('<h2>' + _("Checking for updates...") + '</h2>')
       -            self.detail_label.setText(_("Please wait while Electrum checks for available updates."))
       -
       -
       -class UpdateCheckThread(QThread, PrintError):
       -    checked = pyqtSignal(object)
       -    failed = pyqtSignal()
       -
       -    def __init__(self, main_window):
       -        super().__init__()
       -        self.main_window = main_window
       -
       -    async def get_update_info(self):
       -        async with make_aiohttp_session(proxy=self.main_window.network.proxy) as session:
       -            async with session.get(UpdateCheck.url) as result:
       -                signed_version_dict = await result.json(content_type=None)
       -                # example signed_version_dict:
       -                # {
       -                #     "version": "3.9.9",
       -                #     "signatures": {
       -                #         "1Lqm1HphuhxKZQEawzPse8gJtgjm9kUKT4": "IA+2QG3xPRn4HAIFdpu9eeaCYC7S5wS/sDxn54LJx6BdUTBpse3ibtfq8C43M7M1VfpGkD5tsdwl5C6IfpZD/gQ="
       -                #     }
       -                # }
       -                version_num = signed_version_dict['version']
       -                sigs = signed_version_dict['signatures']
       -                for address, sig in sigs.items():
       -                    if address not in UpdateCheck.VERSION_ANNOUNCEMENT_SIGNING_KEYS:
       -                        continue
       -                    sig = base64.b64decode(sig)
       -                    msg = version_num.encode('utf-8')
       -                    if ecc.verify_message_with_address(address=address, sig65=sig, message=msg,
       -                                                       net=constants.BitcoinMainnet):
       -                        self.print_error(f"valid sig for version announcement '{version_num}' from address '{address}'")
       -                        break
       -                else:
       -                    raise Exception('no valid signature for version announcement')
       -                return StrictVersion(version_num.strip())
       -
       -    def run(self):
       -        network = self.main_window.network
       -        if not network:
       -            self.failed.emit()
       -            return
       -        try:
       -            update_info = asyncio.run_coroutine_threadsafe(self.get_update_info(), network.asyncio_loop).result()
       -        except Exception as e:
       -            #self.print_error(traceback.format_exc())
       -            self.print_error(f"got exception: '{repr(e)}'")
       -            self.failed.emit()
       -        else:
       -            self.checked.emit(update_info)
       -
       -
        if __name__ == "__main__":
            app = QApplication([])
            t = WaitingDialog(None, 'testing ...', lambda: [time.sleep(1)], lambda x: QMessageBox.information(None, 'done', "done"))