diff --git a/basicswap/__init__.py b/basicswap/__init__.py index 75b84e7..f991401 100644 --- a/basicswap/__init__.py +++ b/basicswap/__init__.py @@ -1,3 +1,3 @@ name = "basicswap" -__version__ = "0.11.46" +__version__ = "0.11.47" diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index 87aed59..e718617 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -8,11 +8,13 @@ import os import re import sys import zmq +import copy import json import time import base64 import random import shutil +import string import struct import urllib.request import hashlib @@ -497,6 +499,20 @@ class BasicSwap(BaseApp): raise ValueError('Failed to select a working XMR daemon url.') + def isCoinActive(self, coin): + use_coinid = coin + interface_ind = 'interface' + if coin == Coins.PART_ANON: + use_coinid = Coins.PART + interface_ind = 'interface_anon' + if coin == Coins.PART_BLIND: + use_coinid = Coins.PART + interface_ind = 'interface_blind' + + if use_coinid not in self.coin_clients: + raise ValueError('Unknown coinid {}'.format(int(coin))) + return interface_ind in self.coin_clients[use_coinid] + def ci(self, coin): # Coin interface use_coinid = coin interface_ind = 'interface' @@ -5188,12 +5204,72 @@ class BasicSwap(BaseApp): finally: self.mxDB.release() - def editSettings(self, coin_name, data): - self.log.info('Updating settings %s', coin_name) + def editGeneralSettings(self, data): + self.log.info('Updating general settings') + settings_changed = False + suggest_reboot = False + settings_copy = copy.deepcopy(self.settings) with self.mxDB: - settings_cc = self.settings['chainclients'][coin_name] - settings_changed = False - suggest_reboot = False + if 'debug' in data: + new_value = data['debug'] + ensure(type(new_value) == bool, 'New debug value not boolean') + if settings_copy.get('debug', False) != new_value: + self.debug = new_value + settings_copy['debug'] = new_value + settings_changed = True + + if 'debug_ui' in data: + new_value = data['debug_ui'] + ensure(type(new_value) == bool, 'New debug_ui value not boolean') + if settings_copy.get('debug_ui', False) != new_value: + self.debug_ui = new_value + settings_copy['debug_ui'] = new_value + settings_changed = True + + if 'show_chart' in data: + new_value = data['show_chart'] + ensure(type(new_value) == bool, 'New show_chart value not boolean') + if settings_copy.get('show_chart', True) != new_value: + settings_copy['show_chart'] = new_value + settings_changed = True + + if 'chart_api_key' in data: + new_value = data['chart_api_key'] + ensure(type(new_value) == str, 'New chart_api_key value not a string') + ensure(len(new_value) <= 128, 'New chart_api_key value too long') + if all(c in string.hexdigits for c in new_value): + if settings_copy.get('chart_api_key', '') != new_value: + settings_copy['chart_api_key'] = new_value + if 'chart_api_key_enc' in settings_copy: + settings_copy.pop('chart_api_key_enc') + settings_changed = True + else: + # Encode value as hex to avoid escaping + new_value = new_value.encode('utf-8').hex() + if settings_copy.get('chart_api_key_enc', '') != new_value: + settings_copy['chart_api_key_enc'] = new_value + if 'chart_api_key' in settings_copy: + settings_copy.pop('chart_api_key') + settings_changed = True + + if settings_changed: + + settings_path = os.path.join(self.data_dir, cfg.CONFIG_FILENAME) + settings_path_new = settings_path + '.new' + shutil.copyfile(settings_path, settings_path + '.last') + with open(settings_path_new, 'w') as fp: + json.dump(settings_copy, fp, indent=4) + shutil.move(settings_path_new, settings_path) + self.settings = settings_copy + return settings_changed, suggest_reboot + + def editSettings(self, coin_name, data): + self.log.info(f'Updating settings {coin_name}') + settings_changed = False + suggest_reboot = False + settings_copy = copy.deepcopy(self.settings) + with self.mxDB: + settings_cc = settings_copy['chainclients'][coin_name] if 'lookups' in data: if settings_cc.get('chain_lookups', 'local') != data['lookups']: settings_changed = True @@ -5233,7 +5309,8 @@ class BasicSwap(BaseApp): for coin, cc in self.coin_clients.items(): if cc['name'] == coin_name: cc['fee_priority'] = new_fee_priority - self.ci(coin).setFeePriority(new_fee_priority) + if self.isCoinActive(coin): + self.ci(coin).setFeePriority(new_fee_priority) break if 'conf_target' in data: @@ -5246,7 +5323,8 @@ class BasicSwap(BaseApp): for coin, cc in self.coin_clients.items(): if cc['name'] == coin_name: cc['conf_target'] = new_conf_target - self.ci(coin).setConfTarget(new_conf_target) + if self.isCoinActive(coin): + self.ci(coin).setConfTarget(new_conf_target) break if 'anon_tx_ring_size' in data: @@ -5259,14 +5337,18 @@ class BasicSwap(BaseApp): for coin, cc in self.coin_clients.items(): if cc['name'] == coin_name: cc['anon_tx_ring_size'] = new_anon_tx_ring_size - self.ci(coin).setAnonTxRingSize(new_anon_tx_ring_size) + if self.isCoinActive(coin): + self.ci(coin).setAnonTxRingSize(new_anon_tx_ring_size) break if settings_changed: settings_path = os.path.join(self.data_dir, cfg.CONFIG_FILENAME) + settings_path_new = settings_path + '.new' shutil.copyfile(settings_path, settings_path + '.last') - with open(settings_path, 'w') as fp: - json.dump(self.settings, fp, indent=4) + with open(settings_path_new, 'w') as fp: + json.dump(settings_copy, fp, indent=4) + shutil.move(settings_path_new, settings_path) + self.settings = settings_copy return settings_changed, suggest_reboot def enableCoin(self, coin_name): diff --git a/basicswap/basicswap_util.py b/basicswap/basicswap_util.py index 00b414f..7d691dd 100644 --- a/basicswap/basicswap_util.py +++ b/basicswap/basicswap_util.py @@ -380,6 +380,8 @@ def describeEventEntry(event_type, event_msg): def getVoutByAddress(txjs, p2sh): for o in txjs['vout']: try: + if 'address' in o['scriptPubKey'] and o['scriptPubKey']['address'] == p2sh: + return o['n'] if p2sh in o['scriptPubKey']['addresses']: return o['n'] except Exception: diff --git a/basicswap/http_server.py b/basicswap/http_server.py index eddff58..9240d61 100644 --- a/basicswap/http_server.py +++ b/basicswap/http_server.py @@ -49,7 +49,7 @@ from .ui.page_bids import page_bids, page_bid from .ui.page_offers import page_offers, page_offer, page_newoffer from .ui.page_tor import page_tor, get_tor_established_state from .ui.page_wallet import page_wallets, page_wallet - +from .ui.page_settings import page_settings env = Environment(loader=PackageLoader('basicswap', 'templates')) env.filters['formatts'] = format_timestamp @@ -333,88 +333,6 @@ class HttpHandler(BaseHTTPRequestHandler): 'summary': summary, }) - def page_settings(self, url_split, post_string): - swap_client = self.server.swap_client - swap_client.checkSystemStatus() - summary = swap_client.getSummary() - - messages = [] - err_messages = [] - form_data = self.checkForm(post_string, 'settings', err_messages) - if form_data: - for name, c in swap_client.settings['chainclients'].items(): - if have_data_entry(form_data, 'apply_' + name): - data = {'lookups': get_data_entry(form_data, 'lookups_' + name)} - if name == 'monero': - 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(get_data_entry(form_data, 'conf_target_' + name)) - if name == 'particl': - data['anon_tx_ring_size'] = int(get_data_entry(form_data, 'rct_ring_size_' + name)) - - settings_changed, suggest_reboot = swap_client.editSettings(name, data) - if settings_changed is True: - messages.append('Settings applied.') - if suggest_reboot is True: - messages.append('Please restart BasicSwap.') - elif have_data_entry(form_data, 'enable_' + name): - swap_client.enableCoin(name) - display_name = getCoinName(swap_client.getCoinIdFromName(name)) - messages.append(display_name + ' enabled, shutting down.') - swap_client.stopRunning() - elif have_data_entry(form_data, 'disable_' + name): - swap_client.disableCoin(name) - display_name = getCoinName(swap_client.getCoinIdFromName(name)) - messages.append(display_name + ' disabled, shutting down.') - swap_client.stopRunning() - chains_formatted = [] - - sorted_names = sorted(swap_client.settings['chainclients'].keys()) - for name in sorted_names: - c = swap_client.settings['chainclients'][name] - try: - display_name = getCoinName(swap_client.getCoinIdFromName(name)) - except Exception: - display_name = name - chains_formatted.append({ - 'name': name, - 'display_name': display_name, - 'lookups': c.get('chain_lookups', 'local'), - 'manage_daemon': c.get('manage_daemon', 'Unknown'), - 'connection_type': c.get('connection_type', 'Unknown'), - }) - 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': - chains_formatted[-1]['anon_tx_ring_size'] = c.get('anon_tx_ring_size', 12) - else: - if c.get('connection_type', 'Unknown') == 'none': - if 'connection_type_prev' in c: - chains_formatted[-1]['can_reenable'] = True - else: - chains_formatted[-1]['can_disable'] = True - - template = env.get_template('settings.html') - return self.render_template(template, { - 'messages': messages, - 'err_messages': err_messages, - 'chains': chains_formatted, - 'summary': summary, - }) - def page_watched(self, url_split, post_string): swap_client = self.server.swap_client swap_client.checkSystemStatus() @@ -663,7 +581,7 @@ class HttpHandler(BaseHTTPRequestHandler): if page == 'wallet': return page_wallet(self, url_split, post_string) if page == 'settings': - return self.page_settings(url_split, post_string) + return page_settings(self, url_split, post_string) if page == 'error': return self.page_error(url_split, post_string) if page == 'info': diff --git a/basicswap/templates/offers.html b/basicswap/templates/offers.html index 7000fb9..eea66ca 100644 --- a/basicswap/templates/offers.html +++ b/basicswap/templates/offers.html @@ -43,14 +43,16 @@