Browse Source

Simplify services (watchtower, payserver):

- Do not expose services settings in GUI
 - Use a single netaddress configuration variable.
master
ThomasV 6 years ago
parent
commit
11aaa0b66f
  1. 24
      electrum/daemon.py
  2. 128
      electrum/gui/qt/settings_dialog.py
  3. 2
      electrum/network.py
  4. 10
      electrum/simple_config.py
  5. 7
      electrum/tests/regtest/regtest.sh
  6. 7
      electrum/wallet.py

24
electrum/daemon.py

@ -142,8 +142,9 @@ def get_rpc_credentials(config: SimpleConfig) -> Tuple[str, str]:
class WatchTowerServer(Logger): class WatchTowerServer(Logger):
def __init__(self, network): def __init__(self, network, netaddress):
Logger.__init__(self) Logger.__init__(self)
self.addr = netaddress
self.config = network.config self.config = network.config
self.network = network self.network = network
self.lnwatcher = network.local_watchtower self.lnwatcher = network.local_watchtower
@ -163,11 +164,9 @@ class WatchTowerServer(Logger):
return web.Response() return web.Response()
async def run(self): async def run(self):
host = self.config.get('watchtower_host')
port = self.config.get('watchtower_port', 12345)
self.runner = web.AppRunner(self.app) self.runner = web.AppRunner(self.app)
await self.runner.setup() await self.runner.setup()
site = web.TCPSite(self.runner, host, port, ssl_context=self.config.get_ssl_context()) site = web.TCPSite(self.runner, host=str(self.addr.host), port=self.addr.port, ssl_context=self.config.get_ssl_context())
await site.start() await site.start()
async def get_ctn(self, *args): async def get_ctn(self, *args):
@ -179,8 +178,9 @@ class WatchTowerServer(Logger):
class PayServer(Logger): class PayServer(Logger):
def __init__(self, daemon: 'Daemon'): def __init__(self, daemon: 'Daemon', netaddress):
Logger.__init__(self) Logger.__init__(self)
self.addr = netaddress
self.daemon = daemon self.daemon = daemon
self.config = daemon.config self.config = daemon.config
self.pending = defaultdict(asyncio.Event) self.pending = defaultdict(asyncio.Event)
@ -198,8 +198,6 @@ class PayServer(Logger):
@ignore_exceptions @ignore_exceptions
@log_exceptions @log_exceptions
async def run(self): async def run(self):
host = self.config.get('payserver_host', 'localhost')
port = self.config.get('payserver_port')
root = self.config.get('payserver_root', '/r') root = self.config.get('payserver_root', '/r')
app = web.Application() app = web.Application()
app.add_routes([web.post('/api/create_invoice', self.create_request)]) app.add_routes([web.post('/api/create_invoice', self.create_request)])
@ -209,7 +207,7 @@ class PayServer(Logger):
app.add_routes([web.static(root, os.path.join(os.path.dirname(__file__), 'www'))]) app.add_routes([web.static(root, os.path.join(os.path.dirname(__file__), 'www'))])
runner = web.AppRunner(app) runner = web.AppRunner(app)
await runner.setup() await runner.setup()
site = web.TCPSite(runner, port=port, host=host, ssl_context=self.config.get_ssl_context()) site = web.TCPSite(runner, host=str(self.addr.host), port=self.addr.port, ssl_context=self.config.get_ssl_context())
await site.start() await site.start()
async def create_request(self, request): async def create_request(self, request):
@ -306,13 +304,15 @@ class Daemon(Logger):
daemon_jobs.append(self.start_jsonrpc(config, fd)) daemon_jobs.append(self.start_jsonrpc(config, fd))
# request server # request server
self.pay_server = None self.pay_server = None
if not config.get('offline') and self.config.get('run_payserver'): payserver_address = self.config.get_netaddress('payserver_address')
self.pay_server = PayServer(self) if not config.get('offline') and payserver_address:
self.pay_server = PayServer(self, payserver_address)
daemon_jobs.append(self.pay_server.run()) daemon_jobs.append(self.pay_server.run())
# server-side watchtower # server-side watchtower
self.watchtower = None self.watchtower = None
if not config.get('offline') and self.config.get('run_watchtower'): watchtower_address = self.config.get_netaddress('watchtower_address')
self.watchtower = WatchTowerServer(self.network) if not config.get('offline') and watchtower_address:
self.watchtower = WatchTowerServer(self.network, watchtower_address)
daemon_jobs.append(self.watchtower.run) daemon_jobs.append(self.watchtower.run)
if self.network: if self.network:
self.network.start(jobs=[self.fx.run]) self.network.start(jobs=[self.fx.run])

128
electrum/gui/qt/settings_dialog.py

@ -62,7 +62,6 @@ class SettingsDialog(WindowModalDialog):
fee_widgets = [] fee_widgets = []
tx_widgets = [] tx_widgets = []
oa_widgets = [] oa_widgets = []
services_widgets = []
# language # language
lang_help = _('Select which language is used in the GUI (after restart).') lang_help = _('Select which language is used in the GUI (after restart).')
@ -146,10 +145,19 @@ class SettingsDialog(WindowModalDialog):
# lightning # lightning
lightning_widgets = [] lightning_widgets = []
help_persist = _("""If this option is checked, Electrum will persist as a daemon after help_local_wt = _("""If this option is checked, Electrum will
you close all your wallet windows. Your local watchtower will keep run a local watchtower to watch your channels if your wallet is not
running, and it will protect your channels even if your wallet is not
open. For this to work, your computer needs to be online regularly.""") open. For this to work, your computer needs to be online regularly.""")
local_wt_cb = QCheckBox(_("Run a local watchtower"))
local_wt_cb.setToolTip(help_local_wt)
local_wt_cb.setChecked(bool(self.config.get('run_local_watchtower', False)))
def on_local_wt_checked(x):
self.config.set_key('run_local_watchtower', bool(x))
local_wt_cb.stateChanged.connect(on_local_wt_checked)
lightning_widgets.append((local_wt_cb, None))
help_persist = _("""If this option is checked, Electrum will persist as a daemon after
you close all your wallet windows. Use this to keep your local watchtower running""")
persist_cb = QCheckBox(_("Run as daemon after the GUI is closed")) persist_cb = QCheckBox(_("Run as daemon after the GUI is closed"))
persist_cb.setToolTip(help_persist) persist_cb.setToolTip(help_persist)
persist_cb.setChecked(bool(self.config.get('persist_daemon', False))) persist_cb.setChecked(bool(self.config.get('persist_daemon', False)))
@ -186,60 +194,6 @@ open. For this to work, your computer needs to be online regularly.""")
self.alias_e.editingFinished.connect(self.on_alias_edit) self.alias_e.editingFinished.connect(self.on_alias_edit)
oa_widgets.append((alias_label, self.alias_e)) oa_widgets.append((alias_label, self.alias_e))
# Services
ssl_cert = self.config.get('ssl_certfile')
ssl_cert_label = HelpLabel(_('SSL cert file') + ':', 'certificate file, with intermediate certificates if needed')
self.ssl_cert_e = QPushButton(ssl_cert)
self.ssl_cert_e.clicked.connect(self.select_ssl_certfile)
services_widgets.append((ssl_cert_label, self.ssl_cert_e))
ssl_privkey = self.config.get('ssl_keyfile')
ssl_privkey_label = HelpLabel(_('SSL key file') + ':', '')
self.ssl_privkey_e = QPushButton(ssl_privkey)
self.ssl_privkey_e.clicked.connect(self.select_ssl_privkey)
services_widgets.append((ssl_privkey_label, self.ssl_privkey_e))
ssl_domain_label = HelpLabel(_('SSL domain') + ':', '')
self.ssl_domain_e = QLineEdit('')
self.ssl_domain_e.setReadOnly(True)
services_widgets.append((ssl_domain_label, self.ssl_domain_e))
self.check_ssl_config()
hostname = self.config.get('services_hostname', 'localhost')
hostname_label = HelpLabel(_('Hostname') + ':', 'must match your SSL domain')
self.hostname_e = QLineEdit(hostname)
self.hostname_e.editingFinished.connect(self.on_hostname)
services_widgets.append((hostname_label, self.hostname_e))
payserver_cb = QCheckBox(_("Run PayServer"))
payserver_cb.setToolTip("Configure a port")
payserver_cb.setChecked(bool(self.config.get('run_payserver', False)))
def on_payserver_checked(x):
self.config.set_key('run_payserver', bool(x))
self.payserver_port_e.setEnabled(bool(x))
payserver_cb.stateChanged.connect(on_payserver_checked)
payserver_port = self.config.get('payserver_port', 8002)
self.payserver_port_e = QLineEdit(str(payserver_port))
self.payserver_port_e.editingFinished.connect(self.on_payserver_port)
self.payserver_port_e.setEnabled(self.config.get('run_payserver', False))
services_widgets.append((payserver_cb, self.payserver_port_e))
help_local_wt = _("""To run a watchtower, you must run Electrum on a machine
that is always connected to the internet. Configure a port if you want it to be public.""")
local_wt_cb = QCheckBox(_("Run Watchtower"))
local_wt_cb.setToolTip(help_local_wt)
local_wt_cb.setChecked(bool(self.config.get('run_watchtower', False)))
def on_local_wt_checked(x):
self.config.set_key('run_watchtower', bool(x))
self.local_wt_port_e.setEnabled(bool(x))
local_wt_cb.stateChanged.connect(on_local_wt_checked)
watchtower_port = self.config.get('watchtower_port', '')
self.local_wt_port_e = QLineEdit(str(watchtower_port))
self.local_wt_port_e.setEnabled(self.config.get('run_watchtower', False))
self.local_wt_port_e.editingFinished.connect(self.on_watchtower_port)
services_widgets.append((local_wt_cb, self.local_wt_port_e))
# units # units
units = base_units_list units = base_units_list
msg = (_('Base unit of your wallet.') msg = (_('Base unit of your wallet.')
@ -506,7 +460,6 @@ that is always connected to the internet. Configure a port if you want it to be
(tx_widgets, _('Transactions')), (tx_widgets, _('Transactions')),
(lightning_widgets, _('Lightning')), (lightning_widgets, _('Lightning')),
(fiat_widgets, _('Fiat')), (fiat_widgets, _('Fiat')),
(services_widgets, _('Services')),
(oa_widgets, _('OpenAlias')), (oa_widgets, _('OpenAlias')),
] ]
for widgets, name in tabs_info: for widgets, name in tabs_info:
@ -546,60 +499,3 @@ that is always connected to the internet. Configure a port if you want it to be
self.config.set_key('alias', alias, True) self.config.set_key('alias', alias, True)
if alias: if alias:
self.window.fetch_alias() self.window.fetch_alias()
def select_ssl_certfile(self, b):
name = self.config.get('ssl_certfile', '')
filename, __ = QFileDialog.getOpenFileName(self, "Select your SSL certificate file", name)
if filename:
self.config.set_key('ssl_certfile', filename)
self.ssl_cert_e.setText(filename)
self.check_ssl_config()
def select_ssl_privkey(self, b):
name = self.config.get('ssl_keyfile', '')
filename, __ = QFileDialog.getOpenFileName(self, "Select your SSL private key file", name)
if filename:
self.config.set_key('ssl_keyfile', filename)
self.ssl_cert_e.setText(filename)
self.check_ssl_config()
def check_ssl_config(self):
try:
SSL_identity = self.config.get_ssl_domain()
SSL_error = None
except BaseException as e:
SSL_identity = "error"
SSL_error = repr(e)
self.ssl_domain_e.setText(SSL_identity or "")
s = (ColorScheme.RED if SSL_error else ColorScheme.GREEN).as_stylesheet(True) if SSL_identity else ''
self.ssl_domain_e.setStyleSheet(s)
if SSL_error:
self.ssl_domain_e.setText(SSL_error)
def on_hostname(self):
hostname = str(self.hostname_e.text())
self.config.set_key('services_hostname', hostname, True)
def _get_int_port_from_port_text(self, port_text) -> Optional[int]:
if not port_text:
return
try:
port = int(port_text)
if not (0 < port < 2 ** 16):
raise Exception('port out of range')
except Exception:
self.window.show_error("invalid port")
return
return port
def on_payserver_port(self):
port_text = self.payserver_port_e.text()
port = self._get_int_port_from_port_text(port_text)
if port is None: return
self.config.set_key('payserver_port', port, True)
def on_watchtower_port(self):
port_text = self.payserver_port_e.text()
port = self._get_int_port_from_port_text(port_text)
if port is None: return
self.config.set_key('watchtower_port', port, True)

2
electrum/network.py

@ -328,7 +328,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
self.channel_db = None # type: Optional[ChannelDB] self.channel_db = None # type: Optional[ChannelDB]
self.lngossip = None # type: Optional[LNGossip] self.lngossip = None # type: Optional[LNGossip]
self.local_watchtower = None # type: Optional[WatchTower] self.local_watchtower = None # type: Optional[WatchTower]
if self.config.get('run_watchtower', False): if self.config.get('run_local_watchtower', False):
from . import lnwatcher from . import lnwatcher
self.local_watchtower = lnwatcher.WatchTower(self) self.local_watchtower = lnwatcher.WatchTower(self)
self.local_watchtower.start_network(self) self.local_watchtower.start_network(self)

10
electrum/simple_config.py

@ -9,6 +9,7 @@ from typing import Union, Optional
from numbers import Real from numbers import Real
from copy import deepcopy from copy import deepcopy
from aiorpcx import NetAddress
from . import util from . import util
from . import constants from . import constants
@ -592,6 +593,15 @@ class SimpleConfig(Logger):
SSL_identity = None SSL_identity = None
return SSL_identity return SSL_identity
def get_netaddress(self, key: str) -> Optional[NetAddress]:
text = self.get(key)
if text:
try:
host, port = text.split(':')
return NetAddress(host, port)
except:
pass
def read_user_config(path): def read_user_config(path):
"""Parse and store the user config settings in electrum.conf into user_config[].""" """Parse and store the user config settings in electrum.conf into user_config[]."""

7
electrum/tests/regtest/regtest.sh

@ -317,10 +317,9 @@ fi
if [[ $1 == "configure_test_watchtower" ]]; then if [[ $1 == "configure_test_watchtower" ]]; then
# carol is the watchtower of bob # carol is the watchtower of bob
$carol setconfig --offline run_watchtower true $carol setconfig -o run_local_watchtower true
$carol setconfig --offline watchtower_host 127.0.0.1 $carol setconfig -o watchtower_address 127.0.0.1:12345
$carol setconfig --offline watchtower_port 12345 $bob setconfig -o watchtower_url http://127.0.0.1:12345
$bob setconfig --offline watchtower_url http://127.0.0.1:12345
fi fi
if [[ $1 == "watchtower" ]]; then if [[ $1 == "watchtower" ]]; then

7
electrum/wallet.py

@ -1605,13 +1605,12 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
else: else:
return return
# add URL if we are running a payserver # add URL if we are running a payserver
if self.config.get('run_payserver'): payserver = self.config.get_netaddress('payserver_address')
host = self.config.get('payserver_host', 'localhost') if payserver:
port = self.config.get('payserver_port', 8002)
root = self.config.get('payserver_root', '/r') root = self.config.get('payserver_root', '/r')
use_ssl = bool(self.config.get('ssl_keyfile')) use_ssl = bool(self.config.get('ssl_keyfile'))
protocol = 'https' if use_ssl else 'http' protocol = 'https' if use_ssl else 'http'
base = '%s://%s:%d'%(protocol, host, port) base = '%s://%s:%d'%(protocol, payserver.host, payserver.port)
req['view_url'] = base + root + '/pay?id=' + key req['view_url'] = base + root + '/pay?id=' + key
if use_ssl and 'URI' in req: if use_ssl and 'URI' in req:
request_url = base + '/bip70/' + key + '.bip70' request_url = base + '/bip70/' + key + '.bip70'

Loading…
Cancel
Save