electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 19e60f00bbb1571ee266e51eb1a246c17c451a16
parent 7bb4ea150f8cbc9da3edc589c94883c195a72d9a
Author: ThomasV <thomasv@electrum.org>
Date:   Wed, 30 Jan 2019 17:24:43 +0100

add watchtower_window

Diffstat:
Melectrum/gui/qt/__init__.py | 21+++++++++++++++++++--
Melectrum/gui/qt/channels_list.py | 3+--
Melectrum/gui/qt/main_window.py | 1+
Aelectrum/gui/qt/watchtower_window.py | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Melectrum/lnwatcher.py | 10++++++----
5 files changed, 121 insertions(+), 8 deletions(-)

diff --git a/electrum/gui/qt/__init__.py b/electrum/gui/qt/__init__.py @@ -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() @@ -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) @@ -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')) @@ -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): @@ -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 diff --git a/electrum/gui/qt/channels_list.py b/electrum/gui/qt/channels_list.py @@ -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 @@ -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 diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py @@ -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) diff --git a/electrum/gui/qt/watchtower_window.py b/electrum/gui/qt/watchtower_window.py @@ -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() diff --git a/electrum/lnwatcher.py b/electrum/lnwatcher.py @@ -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: