add watchtower_window

This commit is contained in:
ThomasV 2019-01-30 17:24:43 +01:00
parent 7bb4ea150f
commit 19e60f00bb
5 changed files with 121 additions and 8 deletions

View file

@ -111,6 +111,7 @@ class ElectrumGui(Logger):
self.timer.setInterval(500) # msec self.timer.setInterval(500) # msec
self.nd = None self.nd = None
self.watchtower_window = None
self.network_updated_signal_obj = QNetworkUpdatedSignalObject() self.network_updated_signal_obj = QNetworkUpdatedSignalObject()
self._num_wizards_in_progress = 0 self._num_wizards_in_progress = 0
self._num_wizards_lock = threading.Lock() self._num_wizards_lock = threading.Lock()
@ -149,8 +150,12 @@ class ElectrumGui(Logger):
else: else:
m = self.tray.contextMenu() m = self.tray.contextMenu()
m.clear() 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: 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(_("Show/Hide"), window.show_or_hide)
submenu.addAction(_("Close"), window.close) submenu.addAction(_("Close"), window.close)
m.addAction(_("Dark/Light"), self.toggle_tray_icon) m.addAction(_("Dark/Light"), self.toggle_tray_icon)
@ -180,11 +185,20 @@ class ElectrumGui(Logger):
def close(self): def close(self):
for window in self.windows: for window in self.windows:
window.close() window.close()
if self.watchtower_window:
self.watchtower_window.close()
def new_window(self, path, uri=None): def new_window(self, path, uri=None):
# Use a signal as can be called from daemon thread # Use a signal as can be called from daemon thread
self.app.new_window_signal.emit(path, uri) 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): def show_network_dialog(self, parent):
if not self.daemon.network: 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')) parent.show_warning(_('You are using Electrum in offline mode; restart Electrum if you want to get connected'), title=_('Offline'))
@ -326,6 +340,9 @@ class ElectrumGui(Logger):
self.logger.exception('') self.logger.exception('')
return return
self.timer.start() self.timer.start()
# todo: create this only if channels need it
self.create_watchtower_window()
self.config.open_last_wallet() self.config.open_last_wallet()
path = self.config.get_wallet_path() path = self.config.get_wallet_path()
if not self.start_new_window(path, self.config.get('url'), app_is_starting=True): if not self.start_new_window(path, self.config.get('url'), app_is_starting=True):
@ -338,7 +355,7 @@ class ElectrumGui(Logger):
return return
# check if a wizard is in progress # check if a wizard is in progress
with self._num_wizards_lock: 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 return
self.app.quit() self.app.quit()
self.app.setQuitOnLastWindowClosed(False) # so _we_ can decide whether to quit self.app.setQuitOnLastWindowClosed(False) # so _we_ can decide whether to quit

View file

@ -9,7 +9,7 @@ from electrum.i18n import _
from electrum.lnchan import Channel from electrum.lnchan import Channel
from electrum.lnutil import LOCAL, REMOTE, ConnStringFormatError 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 .amountedit import BTCAmountEdit
from .channel_details import ChannelDetailsDialog from .channel_details import ChannelDetailsDialog
@ -100,7 +100,6 @@ class ChannelsList(MyTreeView):
h = QHBoxLayout() h = QHBoxLayout()
h.addWidget(self.status) h.addWidget(self.status)
h.addStretch() h.addStretch()
h.addWidget(EnterButton(_('Statistics'), self.statistics_dialog))
h.addWidget(EnterButton(_('Open Channel'), self.new_channel_dialog)) h.addWidget(EnterButton(_('Open Channel'), self.new_channel_dialog))
return h return h

View file

@ -627,6 +627,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
# Settings / Preferences are all reserved keywords in macOS using this as work around # 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(_("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(_("&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.addAction(_("&Plugins"), self.plugins_dialog)
tools_menu.addSeparator() tools_menu.addSeparator()
tools_menu.addAction(_("&Sign/verify message"), self.sign_verify_message) tools_menu.addAction(_("&Sign/verify message"), self.sign_verify_message)

View file

@ -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()

View file

@ -58,14 +58,16 @@ class LNWatcher(AddressSynchronizer):
self.network.register_callback(self.on_network_update, self.network.register_callback(self.on_network_update,
['network_updated', 'blockchain_updated', 'verified', 'wallet_updated']) ['network_updated', 'blockchain_updated', 'verified', 'wallet_updated'])
# remote watchtower self.set_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()
# this maps funding_outpoints to ListenerItems, which have an event for when the watcher is done, # 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 # and a queue for seeing which txs are being published
self.tx_progress = {} # type: Dict[str, ListenerItem] 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 with_watchtower(func):
def wrapper(self, *args, **kwargs): def wrapper(self, *args, **kwargs):
if self.watchtower: if self.watchtower: