diff --git a/CHANGELOG.md b/CHANGELOG.md index e360681..3c5d597 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,14 @@ ## Unreleased -- Electrum: Implement `--electrum-skip-merkle` to avoid generating SPV proofs entirely, even when it's possible. (#34) +- Electrum server: Implement `--electrum-skip-merkle` to save some resources by not generating SPV proofs entirely, even when it's possible. (#34) + +- Electrum plugin: Automatically enable `--skipmerklecheck` and `--electrum-skip-merkle`, for better out-of-the-box pruning support and to save some resources. (#34) - Indexer: Use `listsinceblock` instead of `listtransactions`. This makes syncing more bandwidth-efficient and simplifies the implementation. (#33) +- Electrum server: Optimize dispatching notifications to subscribers. + ## 0.1.3 - 2020-06-02 - Electrum: Use dummy SPV proofs to support pruning with the `--skipmerklecheck` option. diff --git a/README.md b/README.md index 27891af..3be6aa9 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,9 @@ You can use bwt with pruning, but: 2. Electrum needs to be run with `--skipmerklecheck` to tolerate missing SPV proofs for transactions in pruned blocks. -If you're running Electrum with `--skipmerklecheck`, you may also configure bwt with `--electrum-skip-merkle` to save some resource by not generating SPV proofs even when it's possible. +> If you're running Electrum with `--skipmerklecheck`, you may also configure bwt with `--electrum-skip-merkle` to save some resources by not generating SPV proofs even when it's possible. +> +> Both of these settings are automatically enabled when using the Electrum plugin. ### Real-time indexing diff --git a/contrib/electrum-plugin/bwt.py b/contrib/electrum-plugin/bwt.py index 1a3bcaa..36fa198 100644 --- a/contrib/electrum-plugin/bwt.py +++ b/contrib/electrum-plugin/bwt.py @@ -20,11 +20,12 @@ bwt_bin = '%s.exe' % bwt_bin class BwtPlugin(BasePlugin): + wallets = set() + proc = None + prev_settings = None def __init__(self, parent, config, name): BasePlugin.__init__(self, parent, config, name) - self.proc = None - self.wallets = set() self.enabled = config.get('bwt_enabled') self.bitcoind_url = config.get('bwt_bitcoind_url', default_bitcoind_url()) @@ -36,15 +37,16 @@ def __init__(self, parent, config, name): self.socket_path = config.get('bwt_socket_path', default_socket_path()) self.verbose = config.get('bwt_verbose', 0) - if config.get('bwt_was_oneserver') is None: - config.set_key('bwt_was_oneserver', config.get('oneserver')) - - self.start() + if self.enabled: + self.set_config() + self.start() def start(self): if not self.enabled or not self.wallets: return + self.stop() + self.rpc_port = free_port() args = [ @@ -52,6 +54,7 @@ def start(self): '--bitcoind-url', self.bitcoind_url, '--bitcoind-dir', self.bitcoind_dir, '--electrum-rpc-addr', '127.0.0.1:%d' % self.rpc_port, + '--electrum-skip-merkle', ] if self.bitcoind_cred: @@ -74,8 +77,9 @@ def start(self): # XXX this doesn't support arguments with spaces. thankfully bwt doesn't currently have any. args.extend(self.custom_opt.split(' ')) - self.stop() - _logger.info('Starting bwt daemon') + self.set_config() + + _logger.info('Starting the bwt daemon') _logger.debug('bwt options: %s' % ' '.join(args)) if platform.system() == 'Windows': @@ -91,12 +95,34 @@ def start(self): def stop(self): if self.proc: - _logger.info('Stopping bwt daemon') + _logger.info('Stopping the bwt daemon') self.proc.terminate() self.proc = None self.thread = None + # enable oneserver/skipmerklecheck and disable manual server selection + def set_config(self): + if self.prev_settings: return # run once + + self.prev_settings = { setting: self.config.cmdline_options.get(setting) + for setting in [ 'oneserver', 'skipmerklecheck', 'server' ] } + + # setting `oneserver`/`skipmerklecheck` directly on `cmdline_options` keeps the settings in-memory only without + # persisting them to the config file, reducing the chance of accidentally leaving them on with public servers. + self.config.cmdline_options['oneserver'] = True + + # for `skipmerklecheck`, this is also the only way to set it an runtime prior to v4 (see https://github.com/spesmilo/electrum/commit/61ccc1ccd3a437d98084089f1d4014ba46c96e3b) + self.config.cmdline_options['skipmerklecheck'] = True + + # set a dummy server so electrum won't attempt connecting to other servers on startup. setting this + # in `cmdline_options` also prevents the user from switching servers using the gui, which further reduces + # the chance of accidentally connecting to public servers with inappropriate settings. + self.config.cmdline_options['server'] = '127.0.0.1:1:t' + def set_server(self): + # first, remove the `server` config to allow `set_parameters()` below to update it and trigger the connection mechanism + del self.config.cmdline_options['server'] + network = Network.get_instance() net_params = network.get_parameters()._replace( host='127.0.0.1', @@ -106,6 +132,9 @@ def set_server(self): ) network.run_from_another_thread(network.set_parameters(net_params)) + # now set the server in `cmdline_options` to lock it in + self.config.cmdline_options['server'] = '127.0.0.1:%s:t' % self.rpc_port + @hook def load_wallet(self, wallet, main_window): if wallet.get_master_public_keys(): @@ -126,11 +155,12 @@ def close(self): BasePlugin.close(self) self.stop() - # restore the user's previous oneserver setting when the plugin is disabled - was_oneserver = self.config.get('bwt_was_oneserver') - if was_oneserver is not None: - self.config.set_key('oneserver', was_oneserver) - self.config.set_key('bwt_was_oneserver', None) + # restore the user's previous settings when the plugin is disabled + if self.prev_settings: + for setting, prev_value in self.prev_settings.items(): + if prev_value is None: self.config.cmdline_options.pop(setting, None) + else: self.config.cmdline_options[setting] = prev_value + self.prev_settings = None def handle_log(self, level, pkg, msg): if msg.startswith('Electrum RPC server running'):