URI: 
       tSimplify services (watchtower, payserver): - Do not expose services settings in GUI - Use a single netaddress configuration variable. - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 11aaa0b66fc2f67fdd6c67099b3d6660450a160d
   DIR parent 526c75ad537b1062292c557d07a7ed342a5e6a22
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Sun, 10 May 2020 14:52:20 +0200
       
       Simplify services (watchtower, payserver):
        - Do not expose services settings in GUI
        - Use a single netaddress configuration variable.
       
       Diffstat:
         M electrum/daemon.py                  |      24 ++++++++++++------------
         M electrum/gui/qt/settings_dialog.py  |     128 +++----------------------------
         M electrum/network.py                 |       2 +-
         M electrum/simple_config.py           |      10 ++++++++++
         M electrum/tests/regtest/regtest.sh   |       7 +++----
         M electrum/wallet.py                  |       7 +++----
       
       6 files changed, 41 insertions(+), 137 deletions(-)
       ---
   DIR diff --git a/electrum/daemon.py b/electrum/daemon.py
       t@@ -142,8 +142,9 @@ def get_rpc_credentials(config: SimpleConfig) -> Tuple[str, str]:
        
        class WatchTowerServer(Logger):
        
       -    def __init__(self, network):
       +    def __init__(self, network, netaddress):
                Logger.__init__(self)
       +        self.addr = netaddress
                self.config = network.config
                self.network = network
                self.lnwatcher = network.local_watchtower
       t@@ -163,11 +164,9 @@ class WatchTowerServer(Logger):
                    return web.Response()
        
            async def run(self):
       -        host = self.config.get('watchtower_host')
       -        port = self.config.get('watchtower_port', 12345)
                self.runner = web.AppRunner(self.app)
                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()
        
            async def get_ctn(self, *args):
       t@@ -179,8 +178,9 @@ class WatchTowerServer(Logger):
        
        class PayServer(Logger):
        
       -    def __init__(self, daemon: 'Daemon'):
       +    def __init__(self, daemon: 'Daemon', netaddress):
                Logger.__init__(self)
       +        self.addr = netaddress
                self.daemon = daemon
                self.config = daemon.config
                self.pending = defaultdict(asyncio.Event)
       t@@ -198,8 +198,6 @@ class PayServer(Logger):
            @ignore_exceptions
            @log_exceptions
            async def run(self):
       -        host = self.config.get('payserver_host', 'localhost')
       -        port = self.config.get('payserver_port')
                root = self.config.get('payserver_root', '/r')
                app = web.Application()
                app.add_routes([web.post('/api/create_invoice', self.create_request)])
       t@@ -209,7 +207,7 @@ class PayServer(Logger):
                app.add_routes([web.static(root, os.path.join(os.path.dirname(__file__), 'www'))])
                runner = web.AppRunner(app)
                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()
        
            async def create_request(self, request):
       t@@ -306,13 +304,15 @@ class Daemon(Logger):
                    daemon_jobs.append(self.start_jsonrpc(config, fd))
                # request server
                self.pay_server = None
       -        if not config.get('offline') and self.config.get('run_payserver'):
       -            self.pay_server = PayServer(self)
       +        payserver_address = self.config.get_netaddress('payserver_address')
       +        if not config.get('offline') and payserver_address:
       +            self.pay_server = PayServer(self, payserver_address)
                    daemon_jobs.append(self.pay_server.run())
                # server-side watchtower
                self.watchtower = None
       -        if not config.get('offline') and self.config.get('run_watchtower'):
       -            self.watchtower = WatchTowerServer(self.network)
       +        watchtower_address = self.config.get_netaddress('watchtower_address')
       +        if not config.get('offline') and watchtower_address:
       +            self.watchtower = WatchTowerServer(self.network, watchtower_address)
                    daemon_jobs.append(self.watchtower.run)
                if self.network:
                    self.network.start(jobs=[self.fx.run])
   DIR diff --git a/electrum/gui/qt/settings_dialog.py b/electrum/gui/qt/settings_dialog.py
       t@@ -62,7 +62,6 @@ class SettingsDialog(WindowModalDialog):
                fee_widgets = []
                tx_widgets = []
                oa_widgets = []
       -        services_widgets = []
        
                # language
                lang_help = _('Select which language is used in the GUI (after restart).')
       t@@ -146,10 +145,19 @@ class SettingsDialog(WindowModalDialog):
                # lightning
                lightning_widgets = []
        
       -        help_persist = _("""If this option is checked, Electrum will persist as a daemon after
       -you close all your wallet windows. Your local watchtower will keep
       -running, and it will protect your channels even if your wallet is not
       +        help_local_wt = _("""If this option is checked, Electrum will
       +run a local watchtower to watch your channels if your wallet is not
        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.setToolTip(help_persist)
                persist_cb.setChecked(bool(self.config.get('persist_daemon', False)))
       t@@ -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)
                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 = base_units_list
                msg = (_('Base unit of your wallet.')
       t@@ -506,7 +460,6 @@ that is always connected to the internet. Configure a port if you want it to be 
                    (tx_widgets, _('Transactions')),
                    (lightning_widgets, _('Lightning')),
                    (fiat_widgets, _('Fiat')),
       -            (services_widgets, _('Services')),
                    (oa_widgets, _('OpenAlias')),
                ]
                for widgets, name in tabs_info:
       t@@ -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)
                if 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)
   DIR diff --git a/electrum/network.py b/electrum/network.py
       t@@ -328,7 +328,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
                self.channel_db = None  # type: Optional[ChannelDB]
                self.lngossip = None  # type: Optional[LNGossip]
                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
                    self.local_watchtower = lnwatcher.WatchTower(self)
                    self.local_watchtower.start_network(self)
   DIR diff --git a/electrum/simple_config.py b/electrum/simple_config.py
       t@@ -9,6 +9,7 @@ from typing import Union, Optional
        from numbers import Real
        
        from copy import deepcopy
       +from aiorpcx import NetAddress
        
        from . import util
        from . import constants
       t@@ -592,6 +593,15 @@ class SimpleConfig(Logger):
                    SSL_identity = None
                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):
            """Parse and store the user config settings in electrum.conf into user_config[]."""
   DIR diff --git a/electrum/tests/regtest/regtest.sh b/electrum/tests/regtest/regtest.sh
       t@@ -317,10 +317,9 @@ fi
        
        if [[ $1 == "configure_test_watchtower" ]]; then
            # carol is the watchtower of bob
       -    $carol setconfig --offline run_watchtower true
       -    $carol setconfig --offline watchtower_host 127.0.0.1
       -    $carol setconfig --offline watchtower_port 12345
       -    $bob setconfig --offline watchtower_url http://127.0.0.1:12345
       +    $carol setconfig -o run_local_watchtower true
       +    $carol setconfig -o watchtower_address 127.0.0.1:12345
       +    $bob setconfig -o watchtower_url http://127.0.0.1:12345
        fi
        
        if [[ $1 == "watchtower" ]]; then
   DIR diff --git a/electrum/wallet.py b/electrum/wallet.py
       t@@ -1605,13 +1605,12 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
                else:
                    return
                # add URL if we are running a payserver
       -        if self.config.get('run_payserver'):
       -            host = self.config.get('payserver_host', 'localhost')
       -            port = self.config.get('payserver_port', 8002)
       +        payserver = self.config.get_netaddress('payserver_address')
       +        if payserver:
                    root = self.config.get('payserver_root', '/r')
                    use_ssl = bool(self.config.get('ssl_keyfile'))
                    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
                    if use_ssl and 'URI' in req:
                        request_url = base + '/bip70/' + key + '.bip70'