URI: 
       tadd watchtower_window - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 19e60f00bbb1571ee266e51eb1a246c17c451a16
   DIR parent 7bb4ea150f8cbc9da3edc589c94883c195a72d9a
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Wed, 30 Jan 2019 17:24:43 +0100
       
       add watchtower_window
       
       Diffstat:
         M electrum/gui/qt/__init__.py         |      21 +++++++++++++++++++--
         M electrum/gui/qt/channels_list.py    |       3 +--
         M electrum/gui/qt/main_window.py      |       1 +
         A electrum/gui/qt/watchtower_window.… |      94 +++++++++++++++++++++++++++++++
         M electrum/lnwatcher.py               |      10 ++++++----
       
       5 files changed, 121 insertions(+), 8 deletions(-)
       ---
   DIR diff --git a/electrum/gui/qt/__init__.py b/electrum/gui/qt/__init__.py
       t@@ -111,6 +111,7 @@ class ElectrumGui(Logger):
                self.timer.setInterval(500)  # msec
        
                self.nd = None
       +        self.watchtower_window = None
                self.network_updated_signal_obj = QNetworkUpdatedSignalObject()
                self._num_wizards_in_progress = 0
                self._num_wizards_lock = threading.Lock()
       t@@ -149,8 +150,12 @@ class ElectrumGui(Logger):
                else:
                    m = self.tray.contextMenu()
                    m.clear()
       +        if self.watchtower_window:
       +            submenu = m.addMenu(_("watchtower"))
       +            submenu.addAction(_("Show/Hide"), self.watchtower_window.show_or_hide)
                for window in self.windows:
       -            submenu = m.addMenu(window.wallet.basename())
       +            name = window.wallet.basename()
       +            submenu = m.addMenu(name)
                    submenu.addAction(_("Show/Hide"), window.show_or_hide)
                    submenu.addAction(_("Close"), window.close)
                m.addAction(_("Dark/Light"), self.toggle_tray_icon)
       t@@ -180,11 +185,20 @@ class ElectrumGui(Logger):
            def close(self):
                for window in self.windows:
                    window.close()
       +        if self.watchtower_window:
       +            self.watchtower_window.close()
        
            def new_window(self, path, uri=None):
                # Use a signal as can be called from daemon thread
                self.app.new_window_signal.emit(path, uri)
        
       +    def create_watchtower_window(self):
       +        from .watchtower_window import WatchTowerWindow
       +        self.watchtower_window = WatchTowerWindow(self)
       +
       +    def show_watchtower_dialog(self, parent):
       +        self.watchtower_window.bring_to_top()
       +
            def show_network_dialog(self, parent):
                if not self.daemon.network:
                    parent.show_warning(_('You are using Electrum in offline mode; restart Electrum if you want to get connected'), title=_('Offline'))
       t@@ -326,6 +340,9 @@ class ElectrumGui(Logger):
                    self.logger.exception('')
                    return
                self.timer.start()
       +        # todo: create this only if channels need it
       +        self.create_watchtower_window()
       +
                self.config.open_last_wallet()
                path = self.config.get_wallet_path()
                if not self.start_new_window(path, self.config.get('url'), app_is_starting=True):
       t@@ -338,7 +355,7 @@ class ElectrumGui(Logger):
                        return
                    # check if a wizard is in progress
                    with self._num_wizards_lock:
       -                if self._num_wizards_in_progress > 0 or len(self.windows) > 0:
       +                if self._num_wizards_in_progress > 0 or len(self.windows) > 0 or self.watchtower_window:
                            return
                    self.app.quit()
                self.app.setQuitOnLastWindowClosed(False)  # so _we_ can decide whether to quit
   DIR diff --git a/electrum/gui/qt/channels_list.py b/electrum/gui/qt/channels_list.py
       t@@ -9,7 +9,7 @@ from electrum.i18n import _
        from electrum.lnchan import Channel
        from electrum.lnutil import LOCAL, REMOTE, ConnStringFormatError
        
       -from .util import MyTreeView, WindowModalDialog, Buttons, OkButton, CancelButton, EnterButton
       +from .util import MyTreeView, WindowModalDialog, Buttons, OkButton, CancelButton, EnterButton, WWLabel
        from .amountedit import BTCAmountEdit
        from .channel_details import ChannelDetailsDialog
        
       t@@ -100,7 +100,6 @@ class ChannelsList(MyTreeView):
                h = QHBoxLayout()
                h.addWidget(self.status)
                h.addStretch()
       -        h.addWidget(EnterButton(_('Statistics'), self.statistics_dialog))
                h.addWidget(EnterButton(_('Open Channel'), self.new_channel_dialog))
                return h
        
   DIR diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py
       t@@ -627,6 +627,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
                # Settings / Preferences are all reserved keywords in macOS using this as work around
                tools_menu.addAction(_("Electrum preferences") if sys.platform == 'darwin' else _("Preferences"), self.settings_dialog)
                tools_menu.addAction(_("&Network"), lambda: self.gui_object.show_network_dialog(self))
       +        tools_menu.addAction(_("&Watchtower"), lambda: self.gui_object.show_watchtower_dialog(self))
                tools_menu.addAction(_("&Plugins"), self.plugins_dialog)
                tools_menu.addSeparator()
                tools_menu.addAction(_("&Sign/verify message"), self.sign_verify_message)
   DIR diff --git a/electrum/gui/qt/watchtower_window.py b/electrum/gui/qt/watchtower_window.py
       t@@ -0,0 +1,94 @@
       +#!/usr/bin/env python
       +#
       +# Electrum - lightweight Bitcoin client
       +# Copyright (C) 2012 thomasv@gitorious
       +#
       +# Permission is hereby granted, free of charge, to any person
       +# obtaining a copy of this software and associated documentation files
       +# (the "Software"), to deal in the Software without restriction,
       +# including without limitation the rights to use, copy, modify, merge,
       +# publish, distribute, sublicense, and/or sell copies of the Software,
       +# and to permit persons to whom the Software is furnished to do so,
       +# subject to the following conditions:
       +#
       +# The above copyright notice and this permission notice shall be
       +# included in all copies or substantial portions of the Software.
       +#
       +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
       +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
       +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
       +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
       +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
       +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
       +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       +# SOFTWARE.
       +
       +import socket
       +
       +from PyQt5.QtGui import *
       +from PyQt5.QtCore import *
       +from PyQt5.QtWidgets import *
       +import PyQt5.QtCore as QtCore
       +
       +from electrum.i18n import _
       +from .util import *
       +
       +help_wt = _("""A watchtower is a process that monitors your channels while you are offline, and prevents the other party from stealing funds in the channel.""")
       +help_local = _("""Electrum runs a watchtower on your computer. This process will persist after you close your wallet. It will not persist if you exit Electrum from the tray menu""")
       +help_remote = _("""To run a remote watchtower, start an electrum daemon on a computer that is always connected to the Internet, and set 'watchtower_host' and 'watchtower_port' in its config""")
       +
       +class WatchTowerWindow(QDialog):
       +
       +    def __init__(self, gui_object):
       +        QDialog.__init__(self)
       +        self.gui_object = gui_object
       +        self.lnwatcher = gui_object.daemon.network.lnwatcher
       +        self.wallet = self.lnwatcher
       +        self.config = gui_object.config
       +        self.setWindowTitle(_('Watchtower'))
       +        self.setMinimumSize(600, 20)
       +        vbox = QVBoxLayout(self)
       +        watchtower_url = self.config.get('watchtower_url')
       +        self.watchtower_e = QLineEdit(watchtower_url)
       +        self.channel_list = QTreeWidget(self)
       +        self.channel_list.setHeaderLabels([_('Node ID'), _('Amount')])
       +
       +        vbox.addWidget(WWLabel(help_wt))
       +        vbox.addStretch(1)
       +        vbox.addWidget(HelpLabel(_('Local Watchtower') + ':', help_local))
       +        vbox.addWidget(self.channel_list)
       +        vbox.addStretch(1)        
       +        g = QGridLayout()
       +        g.addWidget(HelpLabel(_('Remote Watchtower') + ':', help_remote), 1, 0)
       +        g.addWidget(self.watchtower_e, 1, 1)
       +        vbox.addLayout(g)
       +        vbox.addStretch(1)
       +        b = QPushButton(_('Close'))
       +        b.clicked.connect(self.on_close)
       +        vbox.addLayout(Buttons(b))
       +        
       +    def update(self):
       +        pass
       +
       +    def on_close(self):
       +        url = self.watchtower_e.text()
       +        if url:
       +            self.lnwatcher.set_remote_watchtower()
       +        self.hide()
       +
       +    def is_hidden(self):
       +        return self.isMinimized() or self.isHidden()
       +
       +    def show_or_hide(self):
       +        if self.is_hidden():
       +            self.bring_to_top()
       +        else:
       +            self.hide()
       +
       +    def bring_to_top(self):
       +        self.show()
       +        self.raise_()
       +
       +    def closeEvent(self, event):
       +        self.gui_object.watchtower_window = None
       +        event.accept()
   DIR diff --git a/electrum/lnwatcher.py b/electrum/lnwatcher.py
       t@@ -58,14 +58,16 @@ class LNWatcher(AddressSynchronizer):
        
                self.network.register_callback(self.on_network_update,
                                               ['network_updated', 'blockchain_updated', 'verified', 'wallet_updated'])
       -        # remote watchtower
       -        watchtower_url = self.config.get('watchtower_url')
       -        self.watchtower = jsonrpclib.Server(watchtower_url) if watchtower_url else None
       -        self.watchtower_queue = asyncio.Queue()
       +        self.set_remote_watchtower()
                # this maps funding_outpoints to ListenerItems, which have an event for when the watcher is done,
                # and a queue for seeing which txs are being published
                self.tx_progress = {} # type: Dict[str, ListenerItem]
        
       +    def set_remote_watchtower(self):
       +        watchtower_url = self.config.get('watchtower_url')
       +        self.watchtower = jsonrpclib.Server(watchtower_url) if watchtower_url else None
       +        self.watchtower_queue = asyncio.Queue()
       +
            def with_watchtower(func):
                def wrapper(self, *args, **kwargs):
                    if self.watchtower: