From b152150932035063ca77eb6652574ca15723ce08 Mon Sep 17 00:00:00 2001 From: tecnovert Date: Sat, 13 Nov 2021 23:13:54 +0200 Subject: [PATCH] ui, xmr: List of candidate remote XMR daemon urls can be set through the http ui --- basicswap/basicswap.py | 62 ++++++++++++++++++++++++++++++- basicswap/http_server.py | 30 +++++++++++---- basicswap/rpc_xmr.py | 15 ++++++-- basicswap/templates/settings.html | 23 ++++++++++++ doc/release-notes.md | 2 + 5 files changed, 120 insertions(+), 12 deletions(-) diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index c70db9e..543948f 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -34,6 +34,7 @@ from .interface_xmr import XMRInterface from .interface_passthrough_btc import PassthroughBTCInterface from . import __version__ +from .rpc_xmr import make_xmr_rpc2_func from .util import ( TemporaryError, pubkeyToAddress, @@ -377,6 +378,9 @@ class BasicSwap(BaseApp): if self.coin_clients[coin]['connection_type'] == 'rpc': if coin == Coins.XMR: + if chain_client_settings.get('automatically_select_daemon', False): + self.selectXMRRemoteDaemon(coin) + self.coin_clients[coin]['walletrpchost'] = chain_client_settings.get('walletrpchost', '127.0.0.1') self.coin_clients[coin]['walletrpcport'] = chain_client_settings.get('walletrpcport', chainparams[coin][self.chain]['walletrpcport']) if 'walletrpcpassword' in chain_client_settings: @@ -384,6 +388,41 @@ class BasicSwap(BaseApp): else: raise ValueError('Missing XMR wallet rpc credentials.') + def selectXMRRemoteDaemon(self, coin): + self.log.info('Selecting remote XMR daemon.') + chain_client_settings = self.getChainClientSettings(coin) + remote_daemon_urls = chain_client_settings.get('remote_daemon_urls', []) + rpchost = self.coin_clients[coin]['rpchost'] + rpcport = self.coin_clients[coin]['rpcport'] + current_daemon_url = f'{rpchost}:{rpcport}' + if current_daemon_url in remote_daemon_urls: + self.log.info(f'Trying last used url {rpchost}:{rpcport}.') + try: + rpc_cb2 = make_xmr_rpc2_func(rpcport, rpchost) + test = rpc_cb2('get_height', timeout=20)['height'] + return True + except Exception as e: + self.log.warning(f'Failed to set XMR remote daemon to {rpchost}:{rpcport}, {e}') + random.shuffle(remote_daemon_urls) + for url in remote_daemon_urls: + self.log.info(f'Trying url {url}.') + try: + rpchost, rpcport = url.rsplit(':', 1) + rpc_cb2 = make_xmr_rpc2_func(rpcport, rpchost) + test = rpc_cb2('get_height', timeout=20)['height'] + self.coin_clients[coin]['rpchost'] = rpchost + self.coin_clients[coin]['rpcport'] = rpcport + data = { + 'rpchost': rpchost, + 'rpcport': rpcport, + } + self.editSettings(self.coin_clients[coin]['name'], data) + return True + except Exception as e: + self.log.warning(f'Failed to set XMR remote daemon to {url}, {e}') + + raise ValueError('Failed to select a working XMR daemon url.') + def ci(self, coin): # Coin interface if coin == Coins.PART_ANON: return self.coin_clients[Coins.PART]['interface_anon'] @@ -4818,6 +4857,7 @@ class BasicSwap(BaseApp): with self.mxDB: settings_cc = self.settings['chainclients'][coin_name] settings_changed = False + suggest_reboot = False if 'lookups' in data: if settings_cc.get('chain_lookups', 'local') != data['lookups']: settings_changed = True @@ -4827,6 +4867,26 @@ class BasicSwap(BaseApp): cc['chain_lookups'] = data['lookups'] break + for setting in ('manage_daemon', 'rpchost', 'rpcport', 'automatically_select_daemon'): + if setting not in data: + continue + if settings_cc.get(setting) != data[setting]: + settings_changed = True + suggest_reboot = True + settings_cc[setting] = data[setting] + + if 'remotedaemonurls' in data: + remotedaemonurls_in = data['remotedaemonurls'].split('\n') + remotedaemonurls = set() + for url in remotedaemonurls_in: + if url.count(':') > 0: + remotedaemonurls.add(url.strip()) + + if set(settings_cc.get('remote_daemon_urls', [])) != remotedaemonurls: + settings_cc['remote_daemon_urls'] = list(remotedaemonurls) + settings_changed = True + suggest_reboot = True + if 'fee_priority' in data: new_fee_priority = data['fee_priority'] ensure(new_fee_priority >= 0 and new_fee_priority < 4, 'Invalid priority') @@ -4858,7 +4918,7 @@ class BasicSwap(BaseApp): shutil.copyfile(settings_path, settings_path + '.last') with open(settings_path, 'w') as fp: json.dump(self.settings, fp, indent=4) - return settings_changed + return settings_changed, suggest_reboot def enableCoin(self, coin_name): self.log.info('Enabling coin %s', coin_name) diff --git a/basicswap/http_server.py b/basicswap/http_server.py index 93fae8d..f0d11fa 100644 --- a/basicswap/http_server.py +++ b/basicswap/http_server.py @@ -388,26 +388,36 @@ class HttpHandler(BaseHTTPRequestHandler): form_data = self.checkForm(post_string, 'settings', messages) if form_data: for name, c in swap_client.settings['chainclients'].items(): - if bytes('apply_' + name, 'utf-8') in form_data: - data = {'lookups': form_data[bytes('lookups_' + name, 'utf-8')][0].decode('utf-8')} + if have_data_entry(form_data, 'apply_' + name): + data = {'lookups': get_data_entry(form_data, 'lookups_' + name)} if name == 'monero': - data['fee_priority'] = int(form_data[bytes('fee_priority_' + name, 'utf-8')][0]) + data['fee_priority'] = int(get_data_entry(form_data, 'fee_priority_' + name)) + data['manage_daemon'] = True if get_data_entry(form_data, 'managedaemon_' + name) == 'true' else False + data['rpchost'] = get_data_entry(form_data, 'rpchost_' + name) + data['rpcport'] = int(get_data_entry(form_data, 'rpcport_' + name)) + data['remotedaemonurls'] = get_data_entry(form_data, 'remotedaemonurls_' + name) + data['automatically_select_daemon'] = True if get_data_entry(form_data, 'autosetdaemon_' + name) == 'true' else False else: - data['conf_target'] = int(form_data[bytes('conf_target_' + name, 'utf-8')][0]) + data['conf_target'] = int(get_data_entry(form_data, 'conf_target_' + name)) - if swap_client.editSettings(name, data) is True: + settings_changed, suggest_reboot = swap_client.editSettings(name, data) + if settings_changed is True: messages.append('Settings applied.') - elif bytes('enable_' + name, 'utf-8') in form_data: + if suggest_reboot is True: + messages.append('Please restart BasicSwap.') + elif have_data_entry(form_data, 'enable_' + name): swap_client.enableCoin(name) messages.append(name.capitalize() + ' enabled, shutting down.') swap_client.stopRunning() - elif bytes('disable_' + name, 'utf-8') in form_data: + elif have_data_entry(form_data, 'disable_' + name): swap_client.disableCoin(name) messages.append(name.capitalize() + ' disabled, shutting down.') swap_client.stopRunning() chains_formatted = [] - for name, c in swap_client.settings['chainclients'].items(): + sorted_names = sorted(swap_client.settings['chainclients'].keys()) + for name in sorted_names: + c = swap_client.settings['chainclients'][name] chains_formatted.append({ 'name': name, 'lookups': c.get('chain_lookups', 'local'), @@ -417,6 +427,10 @@ class HttpHandler(BaseHTTPRequestHandler): if name == 'monero': chains_formatted[-1]['fee_priority'] = c.get('fee_priority', 0) chains_formatted[-1]['manage_wallet_daemon'] = c.get('manage_wallet_daemon', 'Unknown') + chains_formatted[-1]['rpchost'] = c.get('rpchost', 'localhost') + chains_formatted[-1]['rpcport'] = int(c.get('rpcport', 18081)) + chains_formatted[-1]['remotedaemonurls'] = '\n'.join(c.get('remote_daemon_urls', [])) + chains_formatted[-1]['autosetdaemon'] = c.get('automatically_select_daemon', False) else: chains_formatted[-1]['conf_target'] = c.get('conf_target', 2) if name != 'particl': diff --git a/basicswap/rpc_xmr.py b/basicswap/rpc_xmr.py index 06f038d..0937f93 100644 --- a/basicswap/rpc_xmr.py +++ b/basicswap/rpc_xmr.py @@ -7,7 +7,10 @@ import requests def callrpc_xmr(rpc_port, auth, method, params=[], rpc_host='127.0.0.1', path='json_rpc', timeout=120): # auth is a tuple: (username, password) try: - url = 'http://{}:{}/{}'.format(rpc_host, rpc_port, path) + if rpc_host.count('://') > 0: + url = '{}:{}/{}'.format(rpc_host, rpc_port, path) + else: + url = 'http://{}:{}/{}'.format(rpc_host, rpc_port, path) request_body = { 'method': method, 'params': params, @@ -30,7 +33,10 @@ def callrpc_xmr(rpc_port, auth, method, params=[], rpc_host='127.0.0.1', path='j def callrpc_xmr_na(rpc_port, method, params=[], rpc_host='127.0.0.1', path='json_rpc', timeout=120): try: - url = 'http://{}:{}/{}'.format(rpc_host, rpc_port, path) + if rpc_host.count('://') > 0: + url = '{}:{}/{}'.format(rpc_host, rpc_port, path) + else: + url = 'http://{}:{}/{}'.format(rpc_host, rpc_port, path) request_body = { 'method': method, 'params': params, @@ -53,7 +59,10 @@ def callrpc_xmr_na(rpc_port, method, params=[], rpc_host='127.0.0.1', path='json def callrpc_xmr2(rpc_port, method, params=None, rpc_host='127.0.0.1', timeout=120): try: - url = 'http://{}:{}/{}'.format(rpc_host, rpc_port, method) + if rpc_host.count('://') > 0: + url = '{}:{}/{}'.format(rpc_host, rpc_port, method) + else: + url = 'http://{}:{}/{}'.format(rpc_host, rpc_port, method) headers = { 'Content-Type': 'application/json' } diff --git a/basicswap/templates/settings.html b/basicswap/templates/settings.html index 6d43bc9..32744d8 100644 --- a/basicswap/templates/settings.html +++ b/basicswap/templates/settings.html @@ -15,8 +15,31 @@ Connection Type{{ c.connection_type }} {% endif %} {% if c.manage_daemon is defined %} +{% if c.name == 'monero' %} +Manage Daemon + +Daemon RPC Host +Daemon RPC Port +Remote Daemon Urls
+List of public nodes to use if "Automatically Select Daemon" is true.
+Add one entry per line, eg:
+node.xmr.to:18081
+ + +Automatically Select Daemon + +{% else %} Manage Daemon{{ c.manage_daemon }} {% endif %} +{% endif %} {% if c.manage_wallet_daemon is defined %} Manage Wallet Daemon{{ c.manage_wallet_daemon }} {% endif %} diff --git a/doc/release-notes.md b/doc/release-notes.md index aed113c..2e22f30 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -9,6 +9,8 @@ - xmrswaps: - Setting state to 'Script tx redeemed' will trigger an attempt to redeem the scriptless lock tx. - Node will wait for the chain B lock tx to reach a spendable depth before attempting to spend. +- ui: Sort settings page by coin name. +- ui, xmr: List of candidate remote XMR daemon urls can be set through the http ui 0.0.25