diff --git a/jmclient/jmclient/wallet.py b/jmclient/jmclient/wallet.py index 94122f7..c9b2dc8 100644 --- a/jmclient/jmclient/wallet.py +++ b/jmclient/jmclient/wallet.py @@ -74,6 +74,9 @@ def estimate_tx_fee(ins, outs, txtype='p2pkh', extra_bytes=0): for a transaction with the given number of inputs and outputs, based on information from the blockchain interface. ''' + if jm_single().bc_interface is None: + raise RuntimeError("Cannot estimate transaction fee " + + "without blockchain source.") fee_per_kb = jm_single().bc_interface.estimate_fee_per_kb( jm_single().config.getint("POLICY","tx_fees")) absurd_fee = jm_single().config.getint("POLICY", "absurd_fee_per_kb") diff --git a/jmclient/jmclient/wallet_service.py b/jmclient/jmclient/wallet_service.py index 8d80643..92d99cc 100644 --- a/jmclient/jmclient/wallet_service.py +++ b/jmclient/jmclient/wallet_service.py @@ -42,7 +42,11 @@ class WalletService(Service): self.bci = jm_single().bc_interface # keep track of the quasi-real-time blockheight # (updated in main monitor loop) - self.update_blockheight() + if self.bci is not None: + self.update_blockheight() + else: + jlog.warning("No blockchain source available, " + + "wallet tools will not show correct balances.") self.wallet = wallet self.synced = False @@ -111,8 +115,9 @@ class WalletService(Service): """ Ensures wallet sync is complete before the main event loop starts. """ - d = task.deferLater(reactor, 0.0, self.sync_wallet) - d.addCallback(self.start_wallet_monitoring) + if self.bci is not None: + d = task.deferLater(reactor, 0.0, self.sync_wallet) + d.addCallback(self.start_wallet_monitoring) def register_callbacks(self, callbacks, txinfo, cb_type="all"): """ Register callbacks that will be called by the diff --git a/jmclient/jmclient/wallet_utils.py b/jmclient/jmclient/wallet_utils.py index 27df19c..e080a11 100644 --- a/jmclient/jmclient/wallet_utils.py +++ b/jmclient/jmclient/wallet_utils.py @@ -1410,7 +1410,7 @@ def wallet_tool_main(wallet_root_path): # the service will not be started since this is a synchronous script: wallet_service = WalletService(wallet) - if method not in noscan_methods: + if method not in noscan_methods and jm_single().bc_interface is not None: # if nothing was configured, we override bitcoind's options so that # unconfirmed balance is included in the wallet display by default if 'listunspent_args' not in jm_single().config.options('POLICY'): diff --git a/jmclient/jmclient/yieldgenerator.py b/jmclient/jmclient/yieldgenerator.py index 92c7cee..b18eb90 100644 --- a/jmclient/jmclient/yieldgenerator.py +++ b/jmclient/jmclient/yieldgenerator.py @@ -12,7 +12,7 @@ from jmclient import Maker, jm_single, load_program_config, \ JMClientProtocolFactory, start_reactor, calc_cj_fee, \ WalletService, add_base_options from .wallet_utils import open_test_wallet_maybe, get_wallet_path -from jmbase.support import EXIT_ARGERROR +from jmbase.support import EXIT_ARGERROR, EXIT_FAILURE jlog = get_log() @@ -239,6 +239,11 @@ def ygmain(ygclass, txfee=1000, cjfee_a=200, cjfee_r=0.002, ordertype='swreloffe load_program_config(config_path=options.datadir) + if jm_single().bc_interface is None: + jlog.error("Running yield generator requires configured " + + "blockchain source.") + sys.exit(EXIT_FAILURE) + wallet_path = get_wallet_path(wallet_name, None) wallet = open_test_wallet_maybe( wallet_path, wallet_name, options.mixdepth, diff --git a/scripts/joinmarket-qt.py b/scripts/joinmarket-qt.py index d314ee1..8113593 100755 --- a/scripts/joinmarket-qt.py +++ b/scripts/joinmarket-qt.py @@ -580,6 +580,9 @@ class SpendTab(QWidget): self.startJoin() def startMultiple(self): + if jm_single().bc_interface is None: + log.info("Cannot start join, blockchain source not available.") + return if not self.spendstate.runstate == 'ready': log.info("Cannot start join, already running.") return @@ -659,7 +662,7 @@ class SpendTab(QWidget): try: amount = btc.amount_to_sat(self.amountInput.text()) except ValueError as e: - JMQtMessageBox(parent, e.args[0], title="Error", mbtype="warn") + JMQtMessageBox(self, e.args[0], title="Error", mbtype="warn") return makercount = int(self.numCPInput.text()) mixdepth = int(self.mixdepthInput.text()) @@ -995,6 +998,12 @@ class SpendTab(QWidget): self.tumbler_destaddrs = None def validateSettings(self): + if jm_single().bc_interface is None: + JMQtMessageBox( + self, + "Sending coins not possible without blockchain source.", + mbtype='warn', title="Error") + return False valid, errmsg = validate_address( str(self.addressInput.text().strip())) if not valid: @@ -1318,11 +1327,17 @@ class JMWalletTab(QWidget): self.wallet_name = os.path.basename( mainWindow.wallet_service.get_storage_location()) if total_bal is None: - total_bal = " (syncing..)" + if jm_single().bc_interface is not None: + total_bal = " (syncing..)" + else: + total_bal = " (unknown, no blockchain source available)" self.label1.setText("CURRENT WALLET: " + self.wallet_name + ', total balance: ' + total_bal) l.show() + if jm_single().bc_interface is None and self.wallet_name != 'NONE': + return + for i in range(nm): if walletinfo: mdbalance = mbalances[i] @@ -1728,6 +1743,12 @@ class JMMainWindow(QMainWindow): self.walletRefresh.stop() self.wallet_service = WalletService(wallet) + + if jm_single().bc_interface is None: + self.centralWidget().widget(0).updateWalletInfo( + get_wallet_printout(self.wallet_service)) + return True + # add information callbacks: self.wallet_service.add_restart_callback(self.restartWithMsg) self.wallet_service.autofreeze_warning_cb = self.autofreeze_warning_cb @@ -1936,15 +1957,16 @@ except Exception as e: ]) JMQtMessageBox(None, config_load_error, mbtype='crit', title='failed to load') sys.exit(EXIT_FAILURE) -# Qt does not currently support any functioning without a Core interface: +# Only partial functionality (see wallet info, change config) is possible +# without a blockchain interface. if jm_single().bc_interface is None: - blockchain_error = ''.join(["Joinmarket-Qt requires Bitcoin Core as a blockchain ", - "interface; change the setting of 'blockchain_source' ", - "in the joinmarket.cfg file to a value not equal to ", - "'no-blockchain'; see comments for details."]) - JMQtMessageBox(None, blockchain_error, mbtype='crit', - title='Invalid blockchain source') - sys.exit(EXIT_FAILURE) + blockchain_warning = ''.join([ + "No blockchain source currently configured. ", + "You will be able to see wallet information and change configuration ", + "but other functionality will be limited. ", + "Go to the 'Settings' tab and configure blockchain settings there."]) + JMQtMessageBox(None, blockchain_warning, mbtype='warn', + title='No blockchain source') #refuse to load non-segwit wallet (needs extra work in wallet-utils). if not jm_single().config.get("POLICY", "segwit") == "true": wallet_load_error = ''.join(["Joinmarket-Qt only supports segwit based wallets, ", diff --git a/scripts/tumbler.py b/scripts/tumbler.py index 6fde361..7f050e6 100755 --- a/scripts/tumbler.py +++ b/scripts/tumbler.py @@ -31,6 +31,10 @@ def main(): sys.exit(EXIT_ARGERROR) load_program_config(config_path=options['datadir']) + if jm_single().bc_interface is None: + jmprint('Error: Needs a blockchain source', "error") + sys.exit(EXIT_FAILURE) + check_regtest() #Load the wallet