def wait_for_close(self, bitcoin_process): ''' Wait for user to close bitcion_qt. ''' initial_wait_seconds = 30 normal_wait_seconds = 10 self.log('waiting for bitcoin-qt to be closed by user') while bitcoin_utils.is_bitcoin_qt_running(): max_secs = initial_wait_seconds secs = 0 # wait for bitcoin-qt while bitcoin_utils.is_bitcoin_qt_running() and (secs < max_secs): try: if bitcoin_process is None: sleep(1) else: bitcoin_process.wait(1) except TimeoutExpired: pass secs += 1 max_secs = normal_wait_seconds if bitcoin_utils.is_bitcoin_qt_running(): current_block = self.manager.get_current_block(show_next_backup_time=False) if (current_block is not None and current_block > self.current_block): self.current_block = current_block self.log(f'current block: {self.current_block}') self.log('finished waiting for bitcoin-qt')
def stop_bitcoin_qt(): ''' Stop bitcoin_qt and determine if it ended properly. >>> init_database() >>> stop_bitcoin_qt() ''' seconds = 0 while (bitcoin_utils.is_bitcoin_qt_running() and seconds < 60): try: send_bitcoin_cli_cmd('stop') sleep(5) seconds += 5 except: pass # use brute force if necessary if bitcoin_utils.is_bitcoin_qt_running(): bin_dir = os.path.join(virtualenv_dir(), 'bin') args = [os.path.join(bin_dir, 'killmatch'), bitcoin_utils.bitcoin_qt()] command.run(*args).stdout # give it a little more time to settle down sleep(5) log(f'bitcoin-qt running: {bitcoin_utils.is_bitcoin_qt_running()}')
def start_bitcoin_qt(): ''' Start bitcoin-qt. >>> init_database() >>> start_bitcoin_qt() >>> stop_bitcoin_qt() ''' bin_dir, data_dir = preferences.get_bitcoin_dirs() command_args = [] cmd = os.path.join(bin_dir, bitcoin_utils.bitcoin_qt()) command_args.append(cmd) data_dir = bitcoin_utils.strip_testnet_from_data_dir(data_dir=data_dir) command_args.append(f'-datadir={data_dir}') extra_args = preferences.get_extra_args() if len(extra_args) > 0: for extra_arg in extra_args: command_args.append(extra_arg) command_args.append('-daemon') command.background(*command_args) log(f'running : {command_args}') # give bitcoind time to start secs = 0 while (not bitcoin_utils.is_bitcoin_qt_running() and secs < 5): sleep(1) secs += 1
def wait(arg): ONE_MINUTE = 60 secs = 0 while ((utils.is_bitcoind_running() or utils.is_bitcoin_qt_running()) and secs < ONE_MINUTE): sleep(1) secs += 1 self.log(f'waited {secs} seconds before retrying "{arg}" command')
def stop_bitcoin_core_apps(): ''' Stop all bitcoin core apps. >>> stop_bitcoin_core_apps() ''' if bitcoin_utils.is_bitcoin_qt_running(): stop_bitcoin_qt() # if it's still running, then kill it if bitcoin_utils.is_bitcoin_qt_running(): bin_dir = os.path.join(virtualenv_dir(), 'bin') args = [os.path.join(bin_dir, 'killmatch'), bitcoin_utils.bitcoin_qt()] command.run(*args).stdout if bitcoin_utils.is_bitcoind_running(): stop_bitcoind() # if it's still running, then kill it if bitcoin_utils.is_bitcoind_running(): bin_dir = os.path.join(virtualenv_dir(), 'bin') args = [os.path.join(bin_dir, 'killmatch'), bitcoin_utils.bitcoind()] command.run(*args).stdout
def tearDown(self): # make sure nothing is left running if is_backup_running(): InterruptBackup.as_view()( self.factory.get('/bitcoin/interrupt_backup/')) sleep(20) # make sure bitcoin-qt shuts down; 60 secs may not be enough, # but don't want to waste time waiting needlessly if is_bitcoin_qt_running(): sleep(60) test_utils.stop_bitcoin_qt() # some tests might create a .bitcom in the home dir test_utils.delete_home_bitcoin_subdir(self.home_bitcoin_subdir_exists)
def warn_core_running(request, message=None): ''' Warn that one of the bitcoin core programs is running. >>> from django.test import RequestFactory >>> no_backup_message = bytearray(NO_BACKUP_IF_CORE_RUNNING, encoding='utf-8') >>> factory = RequestFactory() >>> request = factory.get('/bitcoin/preferences/') >>> response = warn_core_running(request) >>> response.status_code == 200 True >>> b'BitcoinD' in response.content True >>> no_backup_message in response.content False >>> request = factory.get('/bitcoin/preferences/') >>> response = warn_core_running(request, message=NO_BACKUP_IF_CORE_RUNNING) >>> response.status_code == 200 True >>> b'BitcoinD' in response.content True >>> no_backup_message in response.content True ''' if bitcoin_utils.is_bitcoin_qt_running(): app = 'Bitcoin-QT' elif bitcoin_utils.is_bitcoin_tx_running(): app = 'Bitcoin-TX' else: app = 'BitcoinD' if message is None: params = {'app': app} else: params = {'app': app, 'more': message} return render(request, 'bitcoin/core_running.html', params)
def send_bitcoin_cli_cmd(self, arg, max_attempts=1): ''' Send a command via bitcoin-cli. >>> from blockchain_backup.bitcoin.tests import utils as test_utils >>> test_utils.init_database() >>> manager = BitcoinManager('blockchain_backup.bitcoin.manager.log') >>> manager.send_bitcoin_cli_cmd('getblockchaininfo') -1 ''' def wait(arg): ONE_MINUTE = 60 secs = 0 while ((utils.is_bitcoind_running() or utils.is_bitcoin_qt_running()) and secs < ONE_MINUTE): sleep(1) secs += 1 self.log(f'waited {secs} seconds before retrying "{arg}" command') command_args = self.get_bitcoin_cli_cmd(arg) attempts = 0 result = -1 while (attempts < max_attempts and result == -1 and (utils.is_bitcoind_running() or utils.is_bitcoin_qt_running())): try: result = command.run(*command_args).stdout if attempts > 0: self.log(f'resent "{arg}" command {attempts} times') except CalledProcessError as cpe: attempts += 1 self.handle_bitcoin_cli_error(arg, cpe) if attempts < max_attempts: wait(arg) return result
def get_page(self, request): global update_task log('trying to update blockchain') clear_action_updates() # check that no other bitcoin-core app is running if (bitcoin_utils.is_bitcoin_qt_running() or bitcoin_utils.is_bitcoin_tx_running() or (bitcoin_utils.is_bitcoind_running() and not updating())): response = warn_core_running(request) # tell user if another blockchain_backup app is running elif bitcoin_utils.is_backup_running( ) or bitcoin_utils.is_restore_running(): response = warn_bcb_app_running(request) # tell user if another task is running elif accessing_wallet() or backing_up() or restoring(): response = warn_bcb_task_running(request) else: NOTICE1 = 'WARNING: Don\'t shut down your computer before you <a class="btn btn-secondary " role="button"' NOTICE2 = 'id="stop_button" href="/bitcoin/interrupt_update/" title="Click to stop updating the blockchain">Stop update</a>' NOTICE = f'{NOTICE1} {NOTICE2}' context = bitcoin_utils.get_blockchain_context() data_dir_ok, error = preferences.data_dir_ok() if not preferences.bin_dir_ok(): context['notice'] = BAD_BIN_DIR log(BAD_BIN_DIR) elif not data_dir_ok: context['notice'] = BAD_DATA_DIR log(error) else: context['header'] = "Updating Bitcoin's blockchain" context[ 'notice'] = 'WARNING: Don\'t shut down your computer before you <a class="btn btn-secondary " role="button" id="stop_button" href="/bitcoin/interrupt_update/" title="Click to stop updating the blockchain">Stop update</a>' context[ 'subnotice'] = 'Shutting down before this window says it is safe <em>could damage the blockchain</em>.' context['progress'] = 'Starting to update the blockchain' context['update_interval'] = '5000' if updating(): log('already updating blockchain') else: from blockchain_backup.bitcoin.update import UpdateTask update_task = UpdateTask() update_task.start() log('UpdateTask started') # make sure the button doesn't appear any more bitcoin_utils.send_socketio_message('button', ' ') log(f'context: {context}') response = render(request, 'bitcoin/update.html', context=context) return response
def get_page(self, request): global accessing_wallet_task log('accessing bitcoin interactively') clear_action_updates() # it's ok if bitcoin-qt is running in our task if (bitcoin_utils.is_bitcoind_running() or bitcoin_utils.is_bitcoin_tx_running() or (bitcoin_utils.is_bitcoin_qt_running() and not accessing_wallet())): response = warn_core_running(request) # tell user if another app is running elif bitcoin_utils.is_backup_running( ) or bitcoin_utils.is_restore_running(): response = warn_bcb_app_running(request) # tell user if another task is running elif updating() or backing_up() or restoring(): response = warn_bcb_task_running(request) else: SUBNOTICE1 = "Waiting until you exit Bitcoin-QT.<p>Bitcoin-QT will start in another window." SUBNOTICE2 = "You can send and receive transactions from that window." SUBNOTICE3 = "After you exit Bitcoin-QT, Blockchain Backup will continue updating the blockchain until it's time to back it up." SUBNOTICE4 = "Don't forget to back up your wallet routinely." SUBNOTICE = f'{SUBNOTICE1} {SUBNOTICE2} {SUBNOTICE3} {SUBNOTICE4}' context = bitcoin_utils.get_blockchain_context() data_dir_ok, error = preferences.data_dir_ok() if not preferences.bin_dir_ok(): context['notice'] = BAD_BIN_DIR elif not data_dir_ok: context['notice'] = BAD_DATA_DIR context['subnotice'] = error else: context[ 'header'] = "Accessing Bitcoin Core Wallet Interactively" context[ 'notice'] = 'WARNING: Do not shut down your computer until Bitcoin-QT stops completely.' context['subnotice'] = SUBNOTICE if accessing_wallet(): log('already accessing wallet') else: from blockchain_backup.bitcoin.access_wallet import AccessWalletTask accessing_wallet_task = AccessWalletTask() accessing_wallet_task.start() log('access wallet started') # make sure the button doesn't appear any more bitcoin_utils.send_socketio_message('button', ' ') response = render(request, 'bitcoin/access_wallet.html', context=context) return response
def format_blockchain_update(self, blockchain_info, data_dir, show_next_backup_time=True): ''' Format the update to the blockchain. >>> manager = BitcoinManager('blockchain_backup.bitcoin.manager.log') >>> blockchain_info = { ... "chain": "main", ... "blocks": 569060, ... "headers": 569164, ... "bestblockhash": "0000000000000000001ded7310261af91403b97bf02e227b26cccc35bde3eccd", ... "difficulty": 6379265451411.053, ... "mediantime": 1553711097, ... "warnings": "" ... } >>> data_dir = '/tmp/bitcoin/data/testnet3' >>> current_block, progress = manager.format_blockchain_update( ... blockchain_info, data_dir, show_next_backup_time=False) >>> current_block == 569060 True >>> progress.find('Next backup in:') > 0 False >>> current_block, progress = manager.format_blockchain_update( ... blockchain_info, data_dir) >>> current_block == 569060 True >>> progress.find('Next backup in:') > 0 True >>> blockchain_info = { ... "chain": "main", ... "blocks": 2, ... "headers": 0, ... "mediantime": 1553711097, ... "warnings": "" ... } >>> data_dir = '/tmp/bitcoin/data/testnet3' >>> manager.format_blockchain_update(blockchain_info, data_dir) (2, None) ''' progress = None last_known_block = int(blockchain_info['headers']) current_block = int(blockchain_info['blocks']) epoch_time = gmtime(blockchain_info['mediantime']) last_block_time = datetime(epoch_time.tm_year, epoch_time.tm_mon, epoch_time.tm_mday, epoch_time.tm_hour, epoch_time.tm_min, epoch_time.tm_sec, tzinfo=utc) if last_known_block > 0: previous_known_block = state.get_last_known_block() if last_known_block > previous_known_block: state.set_last_known_block(last_known_block) state.set_last_block_time(last_block_time) self.new_blocks_found = True time_behind = f'{utils.get_most_recent_confirmation(last_block_time)} ago' remaining_blocks = last_known_block - current_block if remaining_blocks < 0 or not self.new_blocks_found: remaining_blocks = 'Unknown' rows = [] rows.append( self.format_row('Number of blocks to update', remaining_blocks)) rows.append( self.format_row( 'Most recent confirmation', time_behind, title= 'Most recent transaction confirmed on your Bitcoin node')) if show_next_backup_time: status = utils.get_next_backup_in() if status is not None: rows.append(self.format_row('Next backup in', status)) elif current_block > 0: need_backup = utils.need_to_backup(data_dir, current_block) if need_backup and utils.is_bitcoin_qt_running(): rows.append( self.format_row( '<font color="red">Backup needed</font>', 'Stop Bitcoin-QT as soon as possible to protect your blockchain. The backup will start automatically.' )) progress = f"<table cellspacing=\"5\">{''.join(rows)}</table>" return current_block, progress
def run_qt(self): ''' Run bitcon-qt. ''' ok = False error_message = None self.manager.update_menu(constants.DISABLE_ITEM) try: command_args = self.get_launch_args() if command_args is None: ok = False else: self.manager.rename_logs() state.set_start_access_time(now()) self.log(f'starting bitcoin-qt: {command_args}') os.putenv('DISPLAY', ':0.0') if bitcoin_utils.is_bitcoin_qt_running(): bitcoin_pid = get_pid(bitcoin_utils.bitcoin_qt()) bitcoin_process = None else: bitcoin_pid = None bitcoin_process = Popen(command_args) if bitcoin_process is not None or bitcoin_pid is not None: self.wait_for_close(bitcoin_process) state.set_last_access_time(now()) ok = True else: self.manager.update_progress(self.BITCOIN_QT_OTHER_APP_RUNNING) ok = False except CalledProcessError as cpe: ok = False stdout = cpe.stdout if stdout and not isinstance(stdout, str): stdout = stdout.decode() stderr = cpe.stderr if stderr and not isinstance(stderr, str): stderr = stderr.decode() __, error_message, log_message = self.manager.process_bitcoin_cli_error( 'getblockchaininfo', cpe.returncode, stdout, stderr) if error_message is None: error_message = log_message self.log(error_message) except BitcoinException as be: ok = False error_message = str(be) self.log(error_message) except FileNotFoundError as fnfe: ok = False error_message = str(fnfe) self.log(error_message) except Exception: self.log(format_exc()) if ok: # check the logs to make sure everything was ok ok, error_message = self.manager.bitcoin_finished_ok( bitcoin_utils.is_bitcoin_qt_running) if ok: if self.current_block > 0: state.set_last_block_updated(self.current_block) else: if error_message is None: error_message = '' self.manager.update_subnotice('{} {}'.format( self.BITCOIN_QT_ERROR_LABEL, error_message)) self.manager.update_progress(' ') return ok, error_message